diff options
Diffstat (limited to 'compiler')
58 files changed, 1222 insertions, 718 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 2f55a9eaeda..69ba78282f9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -627,9 +627,11 @@ impl Pat { | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)), // Trivial wrappers over inner patterns. - PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => { - s.walk(it) - } + PatKind::Box(s) + | PatKind::Deref(s) + | PatKind::Ref(s, _) + | PatKind::Paren(s) + | PatKind::Guard(s, _) => s.walk(it), // These patterns do not contain subpatterns, skip. PatKind::Wild @@ -839,6 +841,9 @@ pub enum PatKind { // A never pattern `!`. Never, + /// A guard pattern (e.g., `x if guard(x)`). + Guard(P<Pat>, P<Expr>), + /// Parentheses in patterns used for grouping (i.e., `(PAT)`). Paren(P<Pat>), diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 622c260868e..3a4a8ce266e 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1525,6 +1525,10 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) { visit_opt(e2, |e| vis.visit_expr(e)); vis.visit_span(span); } + PatKind::Guard(p, e) => { + vis.visit_pat(p); + vis.visit_expr(e); + } PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { visit_thin_vec(elems, |elem| vis.visit_pat(elem)) } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 2f6998783fa..0b000c8cef8 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -682,6 +682,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_expr, upper_bound); } + PatKind::Guard(subpattern, guard_condition) => { + try_visit!(visitor.visit_pat(subpattern)); + try_visit!(visitor.visit_expr(guard_condition)); + } PatKind::Wild | PatKind::Rest | PatKind::Never => {} PatKind::Err(_guar) => {} PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index ace7bfb5c73..c4bae084a3f 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -114,6 +114,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_range_end(end, e2.is_some()), ); } + // FIXME(guard_patterns): lower pattern guards to HIR + PatKind::Guard(inner, _) => pattern = inner, PatKind::Slice(pats) => break self.lower_pat_slice(pats), PatKind::Rest => { // If we reach here the `..` pattern is not semantically allowed. diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 89311516081..390a575a186 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -556,6 +556,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); + gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); gate_all!(postfix_match, "postfix match is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 479677b0a5a..49e4a559e73 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1709,6 +1709,14 @@ impl<'a> State<'a> { self.print_expr(e, FixupContext::default()); } } + PatKind::Guard(subpat, condition) => { + self.popen(); + self.print_pat(subpat); + self.space(); + self.word_space("if"); + self.print_expr(condition, FixupContext::default()); + self.pclose(); + } PatKind::Slice(elts) => { self.word("["); self.commasep(Inconsistent, elts, |s, p| s.print_pat(p)); diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c11103af476..b42c99e1a6d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1450,6 +1450,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ty::Param(param_ty) => Ok(( generics.type_param(param_ty, tcx), predicate.trait_ref.print_trait_sugared().to_string(), + Some(predicate.trait_ref.def_id), )), _ => Err(()), } @@ -1463,9 +1464,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { tcx, hir_generics, err, - predicates - .iter() - .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)), + predicates.iter().map(|(param, constraint, def_id)| { + (param.name.as_str(), &**constraint, *def_id) + }), None, ); } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index c9486a730e1..9f552b3feb9 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -227,8 +227,6 @@ impl CodegenBackend for CraneliftCodegenBackend { sess: &Session, outputs: &OutputFilenames, ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) { - let _timer = sess.timer("finish_ongoing_codegen"); - ongoing_codegen.downcast::<driver::aot::OngoingCodegen>().unwrap().join(sess, outputs) } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index d8b055137b3..c38c5d4c644 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -352,84 +352,84 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::saturating_add | sym::saturating_sub => { let ty = arg_tys[0]; - match int_type_width_signed(ty, self) { - Some((width, signed)) => match name { - sym::ctlz | sym::cttz => { - let y = self.const_bool(false); - let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[ - args[0].immediate(), - y, - ]); - - self.intcast(ret, llret_ty, false) - } - sym::ctlz_nonzero => { - let y = self.const_bool(true); - let llvm_name = &format!("llvm.ctlz.i{width}"); - let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) - } - sym::cttz_nonzero => { - let y = self.const_bool(true); - let llvm_name = &format!("llvm.cttz.i{width}"); - let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) - } - sym::ctpop => { - let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[args - [0] - .immediate()]); - self.intcast(ret, llret_ty, false) - } - sym::bswap => { - if width == 8 { - args[0].immediate() // byte swap a u8/i8 is just a no-op - } else { - self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[ - args[0].immediate() - ]) - } - } - sym::bitreverse => self - .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[ + if !ty.is_integral() { + tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + span, + name, + ty, + }); + return Ok(()); + } + let (size, signed) = ty.int_size_and_signed(self.tcx); + let width = size.bits(); + match name { + sym::ctlz | sym::cttz => { + let y = self.const_bool(false); + let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[ + args[0].immediate(), + y, + ]); + + self.intcast(ret, llret_ty, false) + } + sym::ctlz_nonzero => { + let y = self.const_bool(true); + let llvm_name = &format!("llvm.ctlz.i{width}"); + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) + } + sym::cttz_nonzero => { + let y = self.const_bool(true); + let llvm_name = &format!("llvm.cttz.i{width}"); + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) + } + sym::ctpop => { + let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[ + args[0].immediate() + ]); + self.intcast(ret, llret_ty, false) + } + sym::bswap => { + if width == 8 { + args[0].immediate() // byte swap a u8/i8 is just a no-op + } else { + self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[ args[0].immediate() - ]), - sym::rotate_left | sym::rotate_right => { - let is_left = name == sym::rotate_left; - let val = args[0].immediate(); - let raw_shift = args[1].immediate(); - // rotate = funnel shift with first two args the same - let llvm_name = - &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); - - // llvm expects shift to be the same type as the values, but rust - // always uses `u32`. - let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); - - self.call_intrinsic(llvm_name, &[val, val, raw_shift]) + ]) } - sym::saturating_add | sym::saturating_sub => { - let is_add = name == sym::saturating_add; - let lhs = args[0].immediate(); - let rhs = args[1].immediate(); - let llvm_name = &format!( - "llvm.{}{}.sat.i{}", - if signed { 's' } else { 'u' }, - if is_add { "add" } else { "sub" }, - width - ); - self.call_intrinsic(llvm_name, &[lhs, rhs]) - } - _ => bug!(), - }, - None => { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { - span, - name, - ty, - }); - return Ok(()); } + sym::bitreverse => self + .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[ + args[0].immediate() + ]), + sym::rotate_left | sym::rotate_right => { + let is_left = name == sym::rotate_left; + let val = args[0].immediate(); + let raw_shift = args[1].immediate(); + // rotate = funnel shift with first two args the same + let llvm_name = + &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); + + // llvm expects shift to be the same type as the values, but rust + // always uses `u32`. + let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); + + self.call_intrinsic(llvm_name, &[val, val, raw_shift]) + } + sym::saturating_add | sym::saturating_sub => { + let is_add = name == sym::saturating_add; + let lhs = args[0].immediate(); + let rhs = args[1].immediate(); + let llvm_name = &format!( + "llvm.{}{}.sat.i{}", + if signed { 's' } else { 'u' }, + if is_add { "add" } else { "sub" }, + width + ); + self.call_intrinsic(llvm_name, &[lhs, rhs]) + } + _ => bug!(), } } @@ -2531,19 +2531,3 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span_bug!(span, "unknown SIMD intrinsic"); } - -// Returns the width of an int Ty, and if it's signed or not -// Returns None if the type is not an integer -// FIXME: there’s multiple of this functions, investigate using some of the already existing -// stuffs. -fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> { - match ty.kind() { - ty::Int(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), true)) - } - ty::Uint(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), false)) - } - _ => None, - } -} diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3dfb86d422d..5235891a18d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -36,7 +36,7 @@ use rustc_codegen_ssa::back::write::{ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; @@ -370,19 +370,14 @@ impl CodegenBackend for LlvmCodegenBackend { (codegen_results, work_products) } - fn link( - &self, - sess: &Session, - codegen_results: CodegenResults, - outputs: &OutputFilenames, - ) -> Result<(), ErrorGuaranteed> { + fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) { use rustc_codegen_ssa::back::link::link_binary; use crate::back::archive::LlvmArchiveBuilderBuilder; // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs) + link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs); } } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ad81ff272c6..f8b3ba79c0d 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -15,7 +15,7 @@ use rustc_ast::CRATE_NODE_ID; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; @@ -71,7 +71,7 @@ pub fn link_binary( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: CodegenResults, outputs: &OutputFilenames, -) -> Result<(), ErrorGuaranteed> { +) { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new(); @@ -119,7 +119,7 @@ pub fn link_binary( &codegen_results, RlibFlavor::Normal, &path, - )? + ) .build(&out_filename); } CrateType::Staticlib => { @@ -129,7 +129,7 @@ pub fn link_binary( &codegen_results, &out_filename, &path, - )?; + ); } _ => { link_natively( @@ -139,7 +139,7 @@ pub fn link_binary( &out_filename, &codegen_results, path.as_ref(), - )?; + ); } } if sess.opts.json_artifact_notifications { @@ -225,8 +225,6 @@ pub fn link_binary( maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module); } }); - - Ok(()) } // Crate type is not passed when calculating the dylibs to include for LTO. In that case all @@ -298,7 +296,7 @@ fn link_rlib<'a>( codegen_results: &CodegenResults, flavor: RlibFlavor, tmpdir: &MaybeTempDir, -) -> Result<Box<dyn ArchiveBuilder + 'a>, ErrorGuaranteed> { +) -> Box<dyn ArchiveBuilder + 'a> { let mut ab = archive_builder_builder.new_archive_builder(sess); let trailing_metadata = match flavor { @@ -374,7 +372,7 @@ fn link_rlib<'a>( { let path = find_native_static_library(filename.as_str(), true, sess); let src = read(path) - .map_err(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }))?; + .unwrap_or_else(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e })); let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src); let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str()); packed_bundled_libs.push(wrapper_file); @@ -392,7 +390,7 @@ fn link_rlib<'a>( codegen_results.crate_info.used_libraries.iter(), tmpdir.as_ref(), true, - )? { + ) { ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); }); @@ -433,7 +431,7 @@ fn link_rlib<'a>( ab.add_file(&lib) } - Ok(ab) + ab } /// Extract all symbols defined in raw-dylib libraries, collated by library name. @@ -445,7 +443,7 @@ fn link_rlib<'a>( fn collate_raw_dylibs<'a>( sess: &Session, used_libraries: impl IntoIterator<Item = &'a NativeLib>, -) -> Result<Vec<(String, Vec<DllImport>)>, ErrorGuaranteed> { +) -> Vec<(String, Vec<DllImport>)> { // Use index maps to preserve original order of imports and libraries. let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default(); @@ -469,15 +467,13 @@ fn collate_raw_dylibs<'a>( } } } - if let Some(guar) = sess.dcx().has_errors() { - return Err(guar); - } - Ok(dylib_table + sess.dcx().abort_if_errors(); + dylib_table .into_iter() .map(|(name, imports)| { (name, imports.into_iter().map(|(_, import)| import.clone()).collect()) }) - .collect()) + .collect() } fn create_dll_import_libs<'a>( @@ -486,8 +482,8 @@ fn create_dll_import_libs<'a>( used_libraries: impl IntoIterator<Item = &'a NativeLib>, tmpdir: &Path, is_direct_dependency: bool, -) -> Result<Vec<PathBuf>, ErrorGuaranteed> { - Ok(collate_raw_dylibs(sess, used_libraries)? +) -> Vec<PathBuf> { + collate_raw_dylibs(sess, used_libraries) .into_iter() .map(|(raw_dylib_name, raw_dylib_imports)| { let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; @@ -537,7 +533,7 @@ fn create_dll_import_libs<'a>( output_path }) - .collect()) + .collect() } /// Create a static archive. @@ -557,7 +553,7 @@ fn link_staticlib( codegen_results: &CodegenResults, out_filename: &Path, tempdir: &MaybeTempDir, -) -> Result<(), ErrorGuaranteed> { +) { info!("preparing staticlib to {:?}", out_filename); let mut ab = link_rlib( sess, @@ -565,7 +561,7 @@ fn link_staticlib( codegen_results, RlibFlavor::StaticlibBase, tempdir, - )?; + ); let mut all_native_libs = vec![]; let res = each_linked_rlib( @@ -656,8 +652,6 @@ fn link_staticlib( print_native_static_libs(sess, &print.out, &all_native_libs, &all_rust_dylibs); } } - - Ok(()) } /// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a @@ -773,7 +767,7 @@ fn link_natively( out_filename: &Path, codegen_results: &CodegenResults, tmpdir: &Path, -) -> Result<(), ErrorGuaranteed> { +) { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); let self_contained_components = self_contained_components(sess, crate_type); @@ -797,7 +791,7 @@ fn link_natively( temp_filename, codegen_results, self_contained_components, - )?; + ); linker::disable_localization(&mut cmd); @@ -1177,8 +1171,6 @@ fn link_natively( ab.add_file(temp_filename); ab.build(out_filename); } - - Ok(()) } fn strip_symbols_with_external_utility( @@ -2232,7 +2224,7 @@ fn linker_with_args( out_filename: &Path, codegen_results: &CodegenResults, self_contained_components: LinkSelfContainedComponents, -) -> Result<Command, ErrorGuaranteed> { +) -> Command { let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let cmd = &mut *super::linker::get_linker( sess, @@ -2356,7 +2348,7 @@ fn linker_with_args( codegen_results.crate_info.used_libraries.iter(), tmpdir, true, - )? { + ) { cmd.add_object(&output_path); } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case @@ -2388,7 +2380,7 @@ fn linker_with_args( native_libraries_from_nonstatics, tmpdir, false, - )? { + ) { cmd.add_object(&output_path); } @@ -2435,7 +2427,7 @@ fn linker_with_args( // to it and remove the option. Currently the last holdout is wasm32-unknown-emscripten. add_post_link_args(cmd, sess, flavor); - Ok(cmd.take_cmd()) + cmd.take_cmd() } fn add_order_independent_options( diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7c0e9cfd5a7..90d48d6ee7e 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1883,7 +1883,11 @@ impl Translate for SharedEmitter { } impl Emitter for SharedEmitter { - fn emit_diagnostic(&mut self, mut diag: rustc_errors::DiagInner) { + fn emit_diagnostic( + &mut self, + mut diag: rustc_errors::DiagInner, + _registry: &rustc_errors::registry::Registry, + ) { // Check that we aren't missing anything interesting when converting to // the cut-down local `DiagInner`. assert_eq!(diag.span, MultiSpan::new()); @@ -2028,8 +2032,6 @@ pub struct OngoingCodegen<B: ExtraBackendMethods> { impl<B: ExtraBackendMethods> OngoingCodegen<B> { pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) { - let _timer = sess.timer("finish_ongoing_codegen"); - self.shared_emitter_main.check(sess, true); let compiled_modules = sess.time("join_worker_thread", || match self.coordinator.join() { Ok(Ok(compiled_modules)) => compiled_modules, diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 7eab889edf0..5b4a51fc301 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -4,7 +4,6 @@ use std::hash::Hash; use rustc_ast::expand::allocator::AllocatorKind; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{DynSend, DynSync}; -use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; use rustc_metadata::creader::MetadataLoaderDyn; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -84,13 +83,8 @@ pub trait CodegenBackend { ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>); /// This is called on the returned [`CodegenResults`] from [`join_codegen`](Self::join_codegen). - fn link( - &self, - sess: &Session, - codegen_results: CodegenResults, - outputs: &OutputFilenames, - ) -> Result<(), ErrorGuaranteed> { - link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs) + fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) { + link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs); } /// Returns `true` if this backend can be safely called from multiple threads. diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 489bb54a6f9..23f2aa4d029 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -140,7 +140,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { err, param_ty.name.as_str(), &constraint, - None, + Some(trait_ref.def_id), None, ); } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 2beec544fad..f54a932e1b6 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -1018,29 +1018,48 @@ where self.allocate_dyn(layout, kind, MemPlaceMeta::None) } - /// Returns a wide MPlace of type `str` to a new 1-aligned allocation. - /// Immutable strings are deduplicated and stored in global memory. - pub fn allocate_str( + /// Allocates a sequence of bytes in the interpreter's memory. + /// For immutable allocations, uses deduplication to reuse existing memory. + /// For mutable allocations, creates a new unique allocation. + pub fn allocate_bytes( &mut self, - str: &str, + bytes: &[u8], + align: Align, kind: MemoryKind<M::MemoryKind>, mutbl: Mutability, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let tcx = self.tcx.tcx; - + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { // Use cache for immutable strings. - let ptr = if mutbl.is_not() { + if mutbl.is_not() { // Use dedup'd allocation function. let salt = M::get_global_alloc_salt(self, None); - let id = tcx.allocate_bytes_dedup(str.as_bytes(), salt); + let id = self.tcx.allocate_bytes_dedup(bytes, salt); // Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation. - M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))? + M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind)) } else { - self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)? - }; - let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self); + // Allocate new memory for mutable data. + self.allocate_bytes_ptr(bytes, align, kind, mutbl) + } + } + + /// Allocates a string in the interpreter's memory with metadata for length. + /// Uses `allocate_bytes` internally but adds string-specific metadata handling. + pub fn allocate_str( + &mut self, + str: &str, + kind: MemoryKind<M::MemoryKind>, + mutbl: Mutability, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + let bytes = str.as_bytes(); + let ptr = self.allocate_bytes(bytes, Align::ONE, kind, mutbl)?; + + // Create length metadata for the string. + let meta = Scalar::from_target_usize(u64::try_from(bytes.len()).unwrap(), self); + + // Get layout for Rust's str type. let layout = self.layout_of(self.tcx.types.str_).unwrap(); + + // Combine pointer and metadata into a wide pointer. interp_ok(self.ptr_with_meta_to_mplace( ptr.into(), MemPlaceMeta::Meta(meta), diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs index 28574457389..2fc767b3750 100644 --- a/compiler/rustc_driver_impl/src/args.rs +++ b/compiler/rustc_driver_impl/src/args.rs @@ -99,10 +99,7 @@ impl Expander { /// If this function is intended to be used with command line arguments, /// `argv[0]` must be removed prior to calling it manually. #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable -pub fn arg_expand_all( - early_dcx: &EarlyDiagCtxt, - at_args: &[String], -) -> Result<Vec<String>, ErrorGuaranteed> { +pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> { let mut expander = Expander::default(); let mut result = Ok(()); for arg in at_args { @@ -110,7 +107,10 @@ pub fn arg_expand_all( result = Err(early_dcx.early_err(format!("failed to load argument file: {err}"))); } } - result.map(|()| expander.finish()) + if let Err(guar) = result { + guar.raise_fatal(); + } + expander.finish() } /// Gets the raw unprocessed command-line arguments as Unicode strings, without doing any further diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b7d64f75bf3..2e01c385a66 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -42,9 +42,7 @@ use rustc_data_structures::profiling::{ }; use rustc_errors::emitter::stderr_destination; use rustc_errors::registry::Registry; -use rustc_errors::{ - ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult, markdown, -}; +use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown}; use rustc_feature::find_gated_cfg; use rustc_interface::util::{self, get_codegen_backend}; use rustc_interface::{Linker, Queries, interface, passes}; @@ -271,14 +269,14 @@ impl<'a> RunCompiler<'a> { } /// Parse args and run the compiler. - pub fn run(self) -> interface::Result<()> { + pub fn run(self) { run_compiler( self.at_args, self.callbacks, self.file_loader, self.make_codegen_backend, self.using_internal_features, - ) + ); } } @@ -290,7 +288,7 @@ fn run_compiler( Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>, >, using_internal_features: Arc<std::sync::atomic::AtomicBool>, -) -> interface::Result<()> { +) { let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); // Throw away the first argument, the name of the binary. @@ -303,9 +301,11 @@ fn run_compiler( // the compiler with @empty_file as argv[0] and no more arguments. let at_args = at_args.get(1..).unwrap_or_default(); - let args = args::arg_expand_all(&default_early_dcx, at_args)?; + let args = args::arg_expand_all(&default_early_dcx, at_args); - let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) }; + let Some(matches) = handle_options(&default_early_dcx, &args) else { + return; + }; let sopts = config::build_session_options(&mut default_early_dcx, &matches); // fully initialize ice path static once unstable options are available as context @@ -313,7 +313,7 @@ fn run_compiler( if let Some(ref code) = matches.opt_str("explain") { handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color); - return Ok(()); + return; } let (odir, ofile) = make_output(&matches); @@ -338,7 +338,7 @@ fn run_compiler( expanded_args: args, }; - let has_input = match make_input(&default_early_dcx, &matches.free)? { + let has_input = match make_input(&default_early_dcx, &matches.free) { Some(input) => { config.input = input; true // has input: normal compilation @@ -358,7 +358,7 @@ fn run_compiler( // printing some information without compiling, or exiting immediately // after parsing, etc. let early_exit = || { - if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) } + sess.dcx().abort_if_errors(); }; // This implements `-Whelp`. It should be handled very early, like @@ -389,22 +389,25 @@ fn run_compiler( } let linker = compiler.enter(|queries| { - let early_exit = || early_exit().map(|_| None); + let early_exit = || { + sess.dcx().abort_if_errors(); + None + }; // Parse the crate root source code (doesn't parse submodules yet) // Everything else is parsed during macro expansion. - queries.parse()?; + queries.parse(); // If pretty printing is requested: Figure out the representation, print it and exit if let Some(pp_mode) = sess.opts.pretty { if pp_mode.needs_ast_map() { - queries.global_ctxt()?.enter(|tcx| { + queries.global_ctxt().enter(|tcx| { tcx.ensure().early_lint_checks(()); pretty::print(sess, pp_mode, pretty::PrintExtra::NeedsAstMap { tcx }); passes::write_dep_info(tcx); }); } else { - let krate = queries.parse()?; + let krate = queries.parse(); pretty::print(sess, pp_mode, pretty::PrintExtra::AfterParsing { krate: &*krate.borrow(), }); @@ -423,17 +426,17 @@ fn run_compiler( } // Make sure name resolution and macro expansion is run. - queries.global_ctxt()?.enter(|tcx| tcx.resolver_for_lowering()); + queries.global_ctxt().enter(|tcx| tcx.resolver_for_lowering()); if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { - queries.global_ctxt()?.enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir)); + queries.global_ctxt().enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir)); } if callbacks.after_expansion(compiler, queries) == Compilation::Stop { return early_exit(); } - queries.global_ctxt()?.enter(|tcx| { + queries.global_ctxt().enter(|tcx| { passes::write_dep_info(tcx); if sess.opts.output_types.contains_key(&OutputType::DepInfo) @@ -446,24 +449,21 @@ fn run_compiler( return early_exit(); } - tcx.analysis(())?; + tcx.ensure().analysis(()); if callbacks.after_analysis(compiler, tcx) == Compilation::Stop { return early_exit(); } - Ok(Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)?)) + Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)) }) - })?; + }); // Linking is done outside the `compiler.enter()` so that the // `GlobalCtxt` within `Queries` can be freed as early as possible. if let Some(linker) = linker { - let _timer = sess.timer("link"); - linker.link(sess, codegen_backend)? + linker.link(sess, codegen_backend); } - - Ok(()) }) } @@ -496,21 +496,17 @@ fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileNa /// Extract input (string or file and optional path) from matches. /// This handles reading from stdin if `-` is provided. -fn make_input( - early_dcx: &EarlyDiagCtxt, - free_matches: &[String], -) -> Result<Option<Input>, ErrorGuaranteed> { +fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option<Input> { match free_matches { - [] => Ok(None), // no input: we will exit early, + [] => None, // no input: we will exit early, [ifile] if ifile == "-" => { // read from stdin as `Input::Str` let mut input = String::new(); if io::stdin().read_to_string(&mut input).is_err() { // Immediately stop compilation if there was an issue reading // the input (for example if the input stream is not UTF-8). - let reported = early_dcx - .early_err("couldn't read from stdin, as it did not contain valid UTF-8"); - return Err(reported); + early_dcx + .early_fatal("couldn't read from stdin, as it did not contain valid UTF-8"); } let name = match env::var("UNSTABLE_RUSTDOC_TEST_PATH") { @@ -526,9 +522,9 @@ fn make_input( Err(_) => FileName::anon_source_code(&input), }; - Ok(Some(Input::Str { name, input })) + Some(Input::Str { name, input }) } - [ifile] => Ok(Some(Input::File(PathBuf::from(ifile)))), + [ifile] => Some(Input::File(PathBuf::from(ifile))), [ifile1, ifile2, ..] => early_dcx.early_fatal(format!( "multiple input filenames provided (first two filenames are `{}` and `{}`)", ifile1, ifile2 @@ -663,9 +659,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { }; } }; - if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() { - FatalError.raise(); - } + compiler.codegen_backend.link(sess, codegen_results, &outputs); } else { dcx.emit_fatal(RlinkNotAFile {}); } @@ -1608,7 +1602,8 @@ pub fn main() -> ! { let exit_code = catch_with_exit_code(|| { RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks) .set_using_internal_features(using_internal_features) - .run() + .run(); + Ok(()) }); if let Some(format) = callbacks.time_passes { diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 0733b8c0b98..5df960be307 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -222,8 +222,8 @@ impl<'tcx> PrintExtra<'tcx> { } pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { - if ppm.needs_analysis() && ex.tcx().analysis(()).is_err() { - FatalError.raise(); + if ppm.needs_analysis() { + ex.tcx().ensure().analysis(()); } let (src, src_name) = get_source(sess); diff --git a/compiler/rustc_error_codes/src/error_codes/E0751.md b/compiler/rustc_error_codes/src/error_codes/E0751.md index 8794f7868f3..825809b229a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0751.md +++ b/compiler/rustc_error_codes/src/error_codes/E0751.md @@ -9,4 +9,4 @@ impl !MyTrait for i32 { } // error! ``` Negative implementations are a promise that the trait will never be implemented -for the given types. Therefore, both cannot exists at the same time. +for the given types. Therefore, both cannot exist at the same time. diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index b4a651b10b1..b337e279400 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -12,6 +12,7 @@ use rustc_span::SourceFile; use rustc_span::source_map::SourceMap; use crate::emitter::FileWithAnnotatedLines; +use crate::registry::Registry; use crate::snippet::Line; use crate::translation::{Translate, to_fluent_args}; use crate::{ @@ -45,7 +46,7 @@ impl Translate for AnnotateSnippetEmitter { impl Emitter for AnnotateSnippetEmitter { /// The entry point for the diagnostics generation - fn emit_diagnostic(&mut self, mut diag: DiagInner) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { let fluent_args = to_fluent_args(diag.args.iter()); let mut suggestions = diag.suggestions.unwrap_tag(); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index a386129e814..1b6c6edcc61 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -27,6 +27,7 @@ use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStr use tracing::{debug, instrument, trace, warn}; use crate::diagnostic::DiagLocation; +use crate::registry::Registry; use crate::snippet::{ Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString, }; @@ -181,7 +182,7 @@ pub type DynEmitter = dyn Emitter + DynSend; /// Emitter trait for emitting errors. pub trait Emitter: Translate { /// Emit a structured diagnostic. - fn emit_diagnostic(&mut self, diag: DiagInner); + fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry); /// Emit a notification that an artifact has been output. /// Currently only supported for the JSON format. @@ -189,7 +190,7 @@ pub trait Emitter: Translate { /// Emit a report about future breakage. /// Currently only supported for the JSON format. - fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>) {} + fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>, _registry: &Registry) {} /// Emit list of unused externs. /// Currently only supported for the JSON format. @@ -500,7 +501,7 @@ impl Emitter for HumanEmitter { self.sm.as_deref() } - fn emit_diagnostic(&mut self, mut diag: DiagInner) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { let fluent_args = to_fluent_args(diag.args.iter()); let mut suggestions = diag.suggestions.unwrap_tag(); @@ -561,7 +562,7 @@ impl Emitter for SilentEmitter { None } - fn emit_diagnostic(&mut self, mut diag: DiagInner) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { if self.emit_fatal_diagnostic && diag.level == Level::Fatal { if let Some(fatal_note) = &self.fatal_note { diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index e3b6dcea892..97df7f9265a 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -44,7 +44,6 @@ mod tests; pub struct JsonEmitter { #[setters(skip)] dst: IntoDynSyncSend<Box<dyn Write + Send>>, - registry: Option<Registry>, #[setters(skip)] sm: Lrc<SourceMap>, fluent_bundle: Option<Lrc<FluentBundle>>, @@ -74,7 +73,6 @@ impl JsonEmitter { ) -> JsonEmitter { JsonEmitter { dst: IntoDynSyncSend(dst), - registry: None, sm, fluent_bundle: None, fallback_bundle, @@ -121,8 +119,8 @@ impl Translate for JsonEmitter { } impl Emitter for JsonEmitter { - fn emit_diagnostic(&mut self, diag: crate::DiagInner) { - let data = Diagnostic::from_errors_diagnostic(diag, self); + fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) { + let data = Diagnostic::from_errors_diagnostic(diag, self, registry); let result = self.emit(EmitTyped::Diagnostic(data)); if let Err(e) = result { panic!("failed to print diagnostics: {e:?}"); @@ -137,7 +135,7 @@ impl Emitter for JsonEmitter { } } - fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>) { + fn emit_future_breakage_report(&mut self, diags: Vec<crate::DiagInner>, registry: &Registry) { let data: Vec<FutureBreakageItem<'_>> = diags .into_iter() .map(|mut diag| { @@ -151,7 +149,7 @@ impl Emitter for JsonEmitter { } FutureBreakageItem { diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic( - diag, self, + diag, self, registry, )), } }) @@ -291,7 +289,11 @@ struct UnusedExterns<'a> { impl Diagnostic { /// Converts from `rustc_errors::DiagInner` to `Diagnostic`. - fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic { + fn from_errors_diagnostic( + diag: crate::DiagInner, + je: &JsonEmitter, + registry: &Registry, + ) -> Diagnostic { let args = to_fluent_args(diag.args.iter()); let sugg_to_diag = |sugg: &CodeSuggestion| { let translated_message = @@ -344,7 +346,7 @@ impl Diagnostic { let code = if let Some(code) = diag.code { Some(DiagnosticCode { code: code.to_string(), - explanation: je.registry.as_ref().unwrap().try_find_description(code).ok(), + explanation: registry.try_find_description(code).ok(), }) } else if let Some(IsLint { name, .. }) = &diag.is_lint { Some(DiagnosticCode { code: name.to_string(), explanation: None }) @@ -382,7 +384,7 @@ impl Diagnostic { } else { OutputTheme::Ascii }) - .emit_diagnostic(diag); + .emit_diagnostic(diag, registry); let buf = Arc::try_unwrap(buf.0).unwrap().into_inner().unwrap(); let buf = String::from_utf8(buf).unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 98200c367f9..6232c875ee8 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -55,7 +55,6 @@ pub use diagnostic_impls::{ }; pub use emitter::ColorConfig; use emitter::{DynEmitter, Emitter, is_case_difference, is_different}; -use registry::Registry; use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; @@ -77,6 +76,8 @@ pub use snippet::Style; pub use termcolor::{Color, ColorSpec, WriteColor}; use tracing::debug; +use crate::registry::Registry; + pub mod annotate_snippet_emitter_writer; pub mod codes; mod diagnostic; @@ -483,6 +484,8 @@ impl<'a> std::ops::Deref for DiagCtxtHandle<'a> { struct DiagCtxtInner { flags: DiagCtxtFlags, + registry: Registry, + /// The error guarantees from all emitted errors. The length gives the error count. err_guars: Vec<ErrorGuaranteed>, /// The error guarantee from all emitted lint errors. The length gives the @@ -619,9 +622,7 @@ impl Drop for DiagCtxtInner { // Important: it is sound to produce an `ErrorGuaranteed` when emitting // delayed bugs because they are guaranteed to be emitted here if // necessary. - if self.err_guars.is_empty() { - self.flush_delayed() - } + self.flush_delayed(); // Sanity check: did we use some of the expensive `trimmed_def_paths` functions // unexpectedly, that is, without producing diagnostics? If so, for debugging purposes, we @@ -664,6 +665,11 @@ impl DiagCtxt { self } + pub fn with_registry(mut self, registry: Registry) -> Self { + self.inner.get_mut().registry = registry; + self + } + pub fn new(emitter: Box<DynEmitter>) -> Self { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } @@ -694,7 +700,7 @@ impl DiagCtxt { struct FalseEmitter; impl Emitter for FalseEmitter { - fn emit_diagnostic(&mut self, _: DiagInner) { + fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) { unimplemented!("false emitter must only used during `wrap_emitter`") } @@ -759,6 +765,7 @@ impl DiagCtxt { let mut inner = self.inner.borrow_mut(); let DiagCtxtInner { flags: _, + registry: _, err_guars, lint_err_guars, delayed_bugs, @@ -964,7 +971,7 @@ impl<'a> DiagCtxtHandle<'a> { self.inner.borrow().has_errors_or_delayed_bugs() } - pub fn print_error_count(&self, registry: &Registry) { + pub fn print_error_count(&self) { let mut inner = self.inner.borrow_mut(); // Any stashed diagnostics should have been handled by @@ -1014,7 +1021,7 @@ impl<'a> DiagCtxtHandle<'a> { .emitted_diagnostic_codes .iter() .filter_map(|&code| { - if registry.try_find_description(code).is_ok() { + if inner.registry.try_find_description(code).is_ok() { Some(code.to_string()) } else { None @@ -1075,10 +1082,10 @@ impl<'a> DiagCtxtHandle<'a> { } pub fn emit_future_breakage_report(&self) { - let mut inner = self.inner.borrow_mut(); + let inner = &mut *self.inner.borrow_mut(); let diags = std::mem::take(&mut inner.future_breakage_diagnostics); if !diags.is_empty() { - inner.emitter.emit_future_breakage_report(diags); + inner.emitter.emit_future_breakage_report(diags, &inner.registry); } } @@ -1409,6 +1416,7 @@ impl DiagCtxtInner { fn new(emitter: Box<DynEmitter>) -> Self { Self { flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() }, + registry: Registry::new(&[]), err_guars: Vec::new(), lint_err_guars: Vec::new(), delayed_bugs: Vec::new(), @@ -1582,7 +1590,7 @@ impl DiagCtxtInner { } self.has_printed = true; - self.emitter.emit_diagnostic(diagnostic); + self.emitter.emit_diagnostic(diagnostic, &self.registry); } if is_error { @@ -1695,7 +1703,13 @@ impl DiagCtxtInner { // eventually happened. assert!(self.stashed_diagnostics.is_empty()); + if !self.err_guars.is_empty() { + // If an error happened already. We shouldn't expose delayed bugs. + return; + } + if self.delayed_bugs.is_empty() { + // Nothing to do. return; } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index e5500c8bba1..6a6496f9827 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -990,7 +990,7 @@ pub fn parse_ast_fragment<'a>( } } AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), - AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_alt( + AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::Yes, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index abc7200699c..bf26b5d25d2 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -505,6 +505,8 @@ declare_features! ( (incomplete, generic_const_items, "1.73.0", Some(113521)), /// Allows registering static items globally, possibly across crates, to iterate over at runtime. (unstable, global_registration, "1.80.0", Some(125119)), + /// Allows using guards in patterns. + (incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index ff449a858d6..2e227ead14a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -279,7 +279,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } else { let mut err = self.dcx().create_err(err); if suggest_constraining_type_param( - tcx, generics, &mut err, &qself_str, &trait_ref, None, None, + tcx, + generics, + &mut err, + &qself_str, + &trait_ref, + Some(best_trait), + None, ) && !identically_named { // We suggested constraining a type parameter, but the associated item on it diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index f6c3e8ebbcb..4ea171ab4a9 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -114,7 +114,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; -use rustc_errors::ErrorGuaranteed; use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize}; use rustc_middle::bug; use rustc_session::config::CrateType; @@ -212,9 +211,9 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu /// The garbage collection will take care of it. /// /// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph -pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuaranteed> { +pub(crate) fn prepare_session_directory(sess: &Session) { if sess.opts.incremental.is_none() { - return Ok(()); + return; } let _timer = sess.timer("incr_comp_prepare_session_directory"); @@ -224,7 +223,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara // {incr-comp-dir}/{crate-name-and-disambiguator} let crate_dir = crate_path(sess); debug!("crate-dir: {}", crate_dir.display()); - create_dir(sess, &crate_dir, "crate")?; + create_dir(sess, &crate_dir, "crate"); // Hack: canonicalize the path *after creating the directory* // because, on windows, long paths can cause problems; @@ -233,7 +232,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara let crate_dir = match try_canonicalize(&crate_dir) { Ok(v) => v, Err(err) => { - return Err(sess.dcx().emit_err(errors::CanonicalizePath { path: crate_dir, err })); + sess.dcx().emit_fatal(errors::CanonicalizePath { path: crate_dir, err }); } }; @@ -248,11 +247,11 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara // Lock the new session directory. If this fails, return an // error without retrying - let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir)?; + let (directory_lock, lock_file_path) = lock_directory(sess, &session_dir); // Now that we have the lock, we can actually create the session // directory - create_dir(sess, &session_dir, "session")?; + create_dir(sess, &session_dir, "session"); // Find a suitable source directory to copy from. Ignore those that we // have already tried before. @@ -266,7 +265,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara ); sess.init_incr_comp_session(session_dir, directory_lock); - return Ok(()); + return; }; debug!("attempting to copy data from source: {}", source_directory.display()); @@ -280,7 +279,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara } sess.init_incr_comp_session(session_dir, directory_lock); - return Ok(()); + return; } else { debug!("copying failed - trying next directory"); @@ -459,21 +458,17 @@ fn generate_session_dir_path(crate_dir: &Path) -> PathBuf { directory_path } -fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(), ErrorGuaranteed> { +fn create_dir(sess: &Session, path: &Path, dir_tag: &str) { match std_fs::create_dir_all(path) { Ok(()) => { debug!("{} directory created successfully", dir_tag); - Ok(()) } - Err(err) => Err(sess.dcx().emit_err(errors::CreateIncrCompDir { tag: dir_tag, path, err })), + Err(err) => sess.dcx().emit_fatal(errors::CreateIncrCompDir { tag: dir_tag, path, err }), } } /// Allocate the lock-file and lock it. -fn lock_directory( - sess: &Session, - session_dir: &Path, -) -> Result<(flock::Lock, PathBuf), ErrorGuaranteed> { +fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) { let lock_file_path = lock_file_path(session_dir); debug!("lock_directory() - lock_file: {}", lock_file_path.display()); @@ -484,15 +479,15 @@ fn lock_directory( true, ) { // the lock should be exclusive - Ok(lock) => Ok((lock, lock_file_path)), + Ok(lock) => (lock, lock_file_path), Err(lock_err) => { let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err); - Err(sess.dcx().emit_err(errors::CreateLock { + sess.dcx().emit_fatal(errors::CreateLock { lock_err, session_dir, is_unsupported_lock, is_cargo: rustc_session::utils::was_invoked_from_cargo(), - })) + }); } } } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index c74804cc798..48df84f3d09 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -11,7 +11,6 @@ use rustc_serialize::Decodable; use rustc_serialize::opaque::MemDecoder; use rustc_session::Session; use rustc_session::config::IncrementalStateAssertion; -use rustc_span::ErrorGuaranteed; use tracing::{debug, warn}; use super::data::*; @@ -182,7 +181,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkPr /// If we are not in incremental compilation mode, returns `None`. /// Otherwise, tries to load the query result cache from disk, /// creating an empty cache if it could not be loaded. -pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> { +pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> { if sess.opts.incremental.is_none() { return None; } @@ -194,19 +193,19 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> { LoadResult::Ok { data: (bytes, start_pos) } => { let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| { sess.dcx().emit_warn(errors::CorruptFile { path: &path }); - OnDiskCache::new_empty(sess.source_map()) + OnDiskCache::new_empty() }); Some(cache) } - _ => Some(OnDiskCache::new_empty(sess.source_map())), + _ => Some(OnDiskCache::new_empty()), } } /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a /// new graph to an incremental session directory. -pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> { +pub fn setup_dep_graph(sess: &Session) -> DepGraph { // `load_dep_graph` can only be called after `prepare_session_directory`. - prepare_session_directory(sess)?; + prepare_session_directory(sess); let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess)); @@ -222,10 +221,9 @@ pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> { }); } - Ok(res - .and_then(|result| { - let (prev_graph, prev_work_products) = result.open(sess); - build_dep_graph(sess, prev_graph, prev_work_products) - }) - .unwrap_or_else(DepGraph::new_disabled)) + res.and_then(|result| { + let (prev_graph, prev_work_products) = result.open(sess); + build_dep_graph(sess, prev_graph, prev_work_products) + }) + .unwrap_or_else(DepGraph::new_disabled) } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 3920d3077d3..07ae24ee6d3 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -5,9 +5,9 @@ use std::sync::Arc; use rustc_ast::{LitKind, MetaItemKind, token}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::jobserver; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::Lrc; -use rustc_data_structures::{defer, jobserver}; use rustc_errors::registry::Registry; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use rustc_lint::LintStore; @@ -441,7 +441,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se temps_dir, }, bundle, - config.registry.clone(), + config.registry, locale_resources, config.lint_caps, target, @@ -492,32 +492,34 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se // There are two paths out of `f`. // - Normal exit. - // - Panic, e.g. triggered by `abort_if_errors`. + // - Panic, e.g. triggered by `abort_if_errors` or a fatal error. // // We must run `finish_diagnostics` in both cases. - let res = { - // If `f` panics, `finish_diagnostics` will run during - // unwinding because of the `defer`. - let sess_abort_guard = defer(|| { - compiler.sess.finish_diagnostics(&config.registry); - }); - - let res = f(&compiler); - - // If `f` doesn't panic, `finish_diagnostics` will run - // normally when `sess_abort_guard` is dropped. - drop(sess_abort_guard); - - // If error diagnostics have been emitted, we can't return an - // error directly, because the return type of this function - // is `R`, not `Result<R, E>`. But we need to communicate the - // errors' existence to the caller, otherwise the caller might - // mistakenly think that no errors occurred and return a zero - // exit code. So we abort (panic) instead, similar to if `f` - // had panicked. + let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&compiler))); + + compiler.sess.finish_diagnostics(); + + // If error diagnostics have been emitted, we can't return an + // error directly, because the return type of this function + // is `R`, not `Result<R, E>`. But we need to communicate the + // errors' existence to the caller, otherwise the caller might + // mistakenly think that no errors occurred and return a zero + // exit code. So we abort (panic) instead, similar to if `f` + // had panicked. + if res.is_ok() { compiler.sess.dcx().abort_if_errors(); + } + + // Also make sure to flush delayed bugs as if we panicked, the + // bugs would be flushed by the Drop impl of DiagCtxt while + // unwinding, which would result in an abort with + // "panic in a destructor during cleanup". + compiler.sess.dcx().flush_delayed(); - res + let res = match res { + Ok(res) => res, + // Resume unwinding if a panic happened. + Err(err) => std::panic::resume_unwind(err), }; let prof = compiler.sess.prof.clone(); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index f53c7b5cc2d..62f35333015 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -33,15 +33,15 @@ use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_ use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{FileName, SourceFileHash, SourceFileHashAlgorithm}; +use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use tracing::{info, instrument}; -use crate::interface::{Compiler, Result}; +use crate::interface::Compiler; use crate::{errors, proc_macro_decls, util}; -pub(crate) fn parse<'a>(sess: &'a Session) -> Result<ast::Crate> { +pub(crate) fn parse<'a>(sess: &'a Session) -> ast::Crate { let krate = sess .time("parse_crate", || { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { @@ -52,13 +52,16 @@ pub(crate) fn parse<'a>(sess: &'a Session) -> Result<ast::Crate> { }); parser.parse_crate_mod() }) - .map_err(|parse_error| parse_error.emit())?; + .unwrap_or_else(|parse_error| { + let guar: ErrorGuaranteed = parse_error.emit(); + guar.raise_fatal(); + }); if sess.opts.unstable_opts.input_stats { input_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1"); } - Ok(krate) + krate } fn pre_expansion_lint<'a>( @@ -712,7 +715,7 @@ pub(crate) fn create_global_ctxt<'tcx>( gcx_cell: &'tcx OnceLock<GlobalCtxt<'tcx>>, arena: &'tcx WorkerLocal<Arena<'tcx>>, hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>, -) -> Result<&'tcx GlobalCtxt<'tcx>> { +) -> &'tcx GlobalCtxt<'tcx> { let sess = &compiler.sess; rustc_builtin_macros::cmdline_attrs::inject( @@ -733,7 +736,7 @@ pub(crate) fn create_global_ctxt<'tcx>( sess.cfg_version, ); let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess)?; + let dep_graph = setup_dep_graph(sess); let cstore = FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); @@ -796,7 +799,7 @@ pub(crate) fn create_global_ctxt<'tcx>( feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); feed.output_filenames(Arc::new(outputs)); }); - Ok(qcx) + qcx }) } @@ -906,7 +909,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { /// Runs the type-checking, region checking and other miscellaneous analysis /// passes on the crate. -fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { +fn analysis(tcx: TyCtxt<'_>, (): ()) { run_required_analyses(tcx); let sess = tcx.sess; @@ -920,7 +923,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { // But we exclude lint errors from this, because lint errors are typically // less serious and we're more likely to want to continue (#87337). if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() { - return Err(guar); + guar.raise_fatal(); } sess.time("misc_checking_3", || { @@ -1048,8 +1051,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { }) } } - - Ok(()) } /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used @@ -1091,12 +1092,12 @@ fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { pub(crate) fn start_codegen<'tcx>( codegen_backend: &dyn CodegenBackend, tcx: TyCtxt<'tcx>, -) -> Result<Box<dyn Any>> { +) -> Box<dyn Any> { // Don't do code generation if there were any errors. Likewise if // there were any delayed bugs, because codegen will likely cause // more ICEs, obscuring the original problem. if let Some(guar) = tcx.sess.dcx().has_errors_or_delayed_bugs() { - return Err(guar); + guar.raise_fatal(); } // Hook for UI tests. @@ -1124,7 +1125,7 @@ pub(crate) fn start_codegen<'tcx>( } } - Ok(codegen) + codegen } fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index cd3a2fb7049..7e3a1332630 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -16,7 +16,7 @@ use rustc_session::Session; use rustc_session::config::{self, OutputFilenames, OutputType}; use crate::errors::FailedWritingFile; -use crate::interface::{Compiler, Result}; +use crate::interface::Compiler; use crate::passes; /// Represent the result of a query. @@ -27,19 +27,17 @@ use crate::passes; /// [`compute`]: Self::compute pub struct Query<T> { /// `None` means no value has been computed yet. - result: RefCell<Option<Result<Steal<T>>>>, + result: RefCell<Option<Steal<T>>>, } impl<T> Query<T> { - fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<QueryResult<'_, T>> { - RefMut::filter_map( + fn compute<F: FnOnce() -> T>(&self, f: F) -> QueryResult<'_, T> { + QueryResult(RefMut::map( self.result.borrow_mut(), - |r: &mut Option<Result<Steal<T>>>| -> Option<&mut Steal<T>> { - r.get_or_insert_with(|| f().map(Steal::new)).as_mut().ok() + |r: &mut Option<Steal<T>>| -> &mut Steal<T> { + r.get_or_insert_with(|| Steal::new(f())) }, - ) - .map_err(|r| *r.as_ref().unwrap().as_ref().map(|_| ()).unwrap_err()) - .map(QueryResult) + )) } } @@ -95,13 +93,13 @@ impl<'tcx> Queries<'tcx> { } } - pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> { + pub fn parse(&self) -> QueryResult<'_, ast::Crate> { self.parse.compute(|| passes::parse(&self.compiler.sess)) } - pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'tcx, &'tcx GlobalCtxt<'tcx>>> { + pub fn global_ctxt(&'tcx self) -> QueryResult<'tcx, &'tcx GlobalCtxt<'tcx>> { self.gcx.compute(|| { - let krate = self.parse()?.steal(); + let krate = self.parse().steal(); passes::create_global_ctxt( self.compiler, @@ -126,8 +124,8 @@ impl Linker { pub fn codegen_and_build_linker( tcx: TyCtxt<'_>, codegen_backend: &dyn CodegenBackend, - ) -> Result<Linker> { - let ongoing_codegen = passes::start_codegen(codegen_backend, tcx)?; + ) -> Linker { + let ongoing_codegen = passes::start_codegen(codegen_backend, tcx); // This must run after monomorphization so that all generic types // have been instantiated. @@ -141,7 +139,7 @@ impl Linker { tcx.sess.code_stats.print_vtable_sizes(crate_name); } - Ok(Linker { + Linker { dep_graph: tcx.dep_graph.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), crate_hash: if tcx.needs_crate_hash() { @@ -150,16 +148,17 @@ impl Linker { None }, ongoing_codegen, - }) + } } - pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) -> Result<()> { - let (codegen_results, work_products) = - codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames); + pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { + let (codegen_results, work_products) = sess.time("finish_ongoing_codegen", || { + codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames) + }); - if let Some(guar) = sess.dcx().has_errors() { - return Err(guar); - } + sess.dcx().abort_if_errors(); + + let _timer = sess.timer("link"); sess.time("serialize_work_products", || { rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products) @@ -178,7 +177,7 @@ impl Linker { .keys() .any(|&i| i == OutputType::Exe || i == OutputType::Metadata) { - return Ok(()); + return; } if sess.opts.unstable_opts.no_link { @@ -189,10 +188,10 @@ impl Linker { &codegen_results, &*self.output_filenames, ) - .map_err(|error| { + .unwrap_or_else(|error| { sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error }) - })?; - return Ok(()); + }); + return; } let _timer = sess.prof.verbose_generic_activity("link_crate"); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 49e6b763590..422629cd11d 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -359,7 +359,6 @@ lint_improper_ctypes_128bit = 128-bit integers don't currently have a known stab lint_improper_ctypes_array_help = consider passing a pointer to the array lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe -lint_improper_ctypes_box = box cannot be represented as a single pointer lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead @@ -377,7 +376,9 @@ lint_improper_ctypes_enum_repr_help = lint_improper_ctypes_enum_repr_reason = enum has no representation hint lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead +lint_improper_ctypes_fnptr_indirect_reason = the function pointer to `{$ty}` is FFI-unsafe due to `{$inner_ty}` lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention + lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants @@ -388,7 +389,11 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent lint_improper_ctypes_pat_help = consider using the base type instead lint_improper_ctypes_pat_reason = pattern types have no C equivalent -lint_improper_ctypes_slice_help = consider using a raw pointer instead + +lint_improper_ctypes_sized_ptr_to_unsafe_type = + this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout + +lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead lint_improper_ctypes_slice_reason = slices have no C equivalent lint_improper_ctypes_str_help = consider using `*const u8` and a length instead @@ -414,6 +419,10 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_improper_ctypes_unsized_box = this box for an unsized type contains metadata, which makes it incompatible with a C pointer +lint_improper_ctypes_unsized_ptr = this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer +lint_improper_ctypes_unsized_ref = this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + lint_incomplete_include = include macro expected single expression in source diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 4f3184f1d7c..a68a2a7f983 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -245,6 +245,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { self.check_id(lt.id); + ast_visit::walk_lifetime(self, lt); } fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) { @@ -259,6 +260,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_attribute(&mut self, attr: &'a ast::Attribute) { lint_callback!(self, check_attribute, attr); + ast_visit::walk_attribute(self, attr); } fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index 025fd452040..28368e1ab46 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -9,6 +9,7 @@ use crate::lints::{ use crate::{EarlyContext, EarlyLintPass, LintContext}; declare_lint! { + #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the /// visual representation of text on screen in a way that does not correspond to their on /// memory representation. diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 20822f23bf1..9fa263799eb 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1851,13 +1851,44 @@ pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> { pub right: Span, } +pub(crate) struct ImproperCTypesLayer<'a> { + pub ty: Ty<'a>, + pub inner_ty: Option<Ty<'a>>, + pub note: DiagMessage, + pub span_note: Option<Span>, + pub help: Option<DiagMessage>, +} + +impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + f: &F, + ) { + diag.arg("ty", self.ty); + if let Some(ty) = self.inner_ty { + diag.arg("inner_ty", ty); + } + + if let Some(help) = self.help { + let msg = f(diag, help.into()); + diag.help(msg); + } + + let msg = f(diag, self.note.into()); + diag.note(msg); + if let Some(note) = self.span_note { + let msg = f(diag, fluent::lint_note.into()); + diag.span_note(note, msg); + }; + } +} + pub(crate) struct ImproperCTypes<'a> { pub ty: Ty<'a>, pub desc: &'a str, pub label: Span, - pub help: Option<DiagMessage>, - pub note: DiagMessage, - pub span_note: Option<Span>, + pub reasons: Vec<ImproperCTypesLayer<'a>>, } // Used because of the complexity of Option<DiagMessage>, DiagMessage, and Option<Span> @@ -1867,12 +1898,8 @@ impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> { diag.arg("ty", self.ty); diag.arg("desc", self.desc); diag.span_label(self.label, fluent::lint_label); - if let Some(help) = self.help { - diag.help(help); - } - diag.note(self.note); - if let Some(note) = self.span_note { - diag.span_note(note, fluent::lint_note); + for reason in self.reasons.into_iter() { + diag.subdiagnostic(reason); } } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 33650be056d..90d44371ab5 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -22,10 +22,10 @@ mod improper_ctypes; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, - AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, - InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, - UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, - VariantSizeDifferencesDiag, + AtomicOrderingStore, ImproperCTypes, ImproperCTypesLayer, InvalidAtomicOrderingDiag, + InvalidNanComparisons, InvalidNanComparisonsSuggestion, + UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion, + UnusedComparisons, VariantSizeDifferencesDiag, }; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -727,7 +727,109 @@ struct CTypesVisitorState<'tcx> { enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), - FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> }, + FfiUnsafe { + ty: Ty<'tcx>, + reason: DiagMessage, + help: Option<DiagMessage>, + }, + FfiUnsafeWrapper { + ty: Ty<'tcx>, + reason: DiagMessage, + help: Option<DiagMessage>, + wrapped: Box<FfiResult<'tcx>>, + }, +} + +/// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it +#[derive(Clone, Copy)] +enum TypeSizedness { + /// type of definite size (pointers are C-compatible) + Definite, + /// unsized type because it includes an opaque/foreign type (pointers are C-compatible) + UnsizedWithExternType, + /// unsized type for other reasons (slice, string, dyn Trait, closure, ...) (pointers are not C-compatible) + UnsizedWithMetadata, +} + +/// Is this type unsized because it contains (or is) a foreign type? +/// (Returns Err if the type happens to be sized after all) +fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness { + let tcx = cx.tcx; + + if ty.is_sized(tcx, cx.typing_env()) { + TypeSizedness::Definite + } else { + match ty.kind() { + ty::Slice(_) => TypeSizedness::UnsizedWithMetadata, + ty::Str => TypeSizedness::UnsizedWithMetadata, + ty::Dynamic(..) => TypeSizedness::UnsizedWithMetadata, + ty::Foreign(..) => TypeSizedness::UnsizedWithExternType, + // While opaque types are checked for earlier, if a projection in a struct field + // normalizes to an opaque type, then it will reach this branch. + ty::Alias(ty::Opaque, ..) => todo!("We... don't know enough about this type yet?"), + ty::Adt(def, args) => { + // for now assume: boxes and phantoms don't mess with this + match def.adt_kind() { + AdtKind::Union | AdtKind::Enum => { + bug!("unions and enums are necessarily sized") + } + AdtKind::Struct => { + if let Some(sym::cstring_type | sym::cstr_type) = + tcx.get_diagnostic_name(def.did()) + { + return TypeSizedness::UnsizedWithMetadata; + } + // FIXME: how do we deal with non-exhaustive unsized structs/unions? + + if def.non_enum_variant().fields.is_empty() { + bug!("an empty struct is necessarily sized"); + } + + let variant = def.non_enum_variant(); + + // only the last field may be unsized + let n_fields = variant.fields.len(); + let last_field = &variant.fields[(n_fields - 1).into()]; + let field_ty = last_field.ty(cx.tcx, args); + let field_ty = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), field_ty) + .unwrap_or(field_ty); + match get_type_sizedness(cx, field_ty) { + s @ (TypeSizedness::UnsizedWithMetadata + | TypeSizedness::UnsizedWithExternType) => s, + TypeSizedness::Definite => { + bug!("failed to find the reason why struct `{:?}` is unsized", ty) + } + } + } + } + } + ty::Tuple(tuple) => { + // only the last field may be unsized + let n_fields = tuple.len(); + let field_ty: Ty<'tcx> = tuple[n_fields - 1]; + //let field_ty = last_field.ty(cx.tcx, args); + let field_ty = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), field_ty) + .unwrap_or(field_ty); + match get_type_sizedness(cx, field_ty) { + s @ (TypeSizedness::UnsizedWithMetadata + | TypeSizedness::UnsizedWithExternType) => s, + TypeSizedness::Definite => { + bug!("failed to find the reason why tuple `{:?}` is unsized", ty) + } + } + } + ty => { + bug!( + "we shouldn't be trying to determine if this is unsized for a reason or another: `{:?}`", + ty + ) + } + } + } } pub(crate) fn nonnull_optimization_guaranteed<'tcx>( @@ -764,7 +866,7 @@ fn ty_is_known_nonnull<'tcx>( match ty.kind() { ty::FnPtr(..) => true, ty::Ref(..) => true, - ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true, + ty::Adt(def, _) if def.is_box() => true, ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => { let marked_non_null = nonnull_optimization_guaranteed(tcx, *def); @@ -933,12 +1035,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Check if the type is array and emit an unsafe type lint. fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { if let ty::Array(..) = ty.kind() { - self.emit_ffi_unsafe_type_lint( + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { ty, - sp, - fluent::lint_improper_ctypes_array_reason, - Some(fluent::lint_improper_ctypes_array_help), - ); + note: fluent::lint_improper_ctypes_array_reason, + help: Some(fluent::lint_improper_ctypes_array_help), + inner_ty: None, + span_note: None, + }]); true } else { false @@ -995,9 +1098,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { all_phantom &= match self.check_field_type_for_ffi(acc, field, args) { FfiSafe => false, // `()` fields are FFI-safe! - FfiUnsafe { ty, .. } if ty.is_unit() => false, + FfiUnsafe { ty, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false, FfiPhantom(..) => true, - r @ FfiUnsafe { .. } => return r, + r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r, } } @@ -1031,16 +1134,47 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match *ty.kind() { ty::Adt(def, args) => { - if let Some(boxed) = ty.boxed_ty() - && matches!(self.mode, CItemKind::Definition) - { - if boxed.is_sized(tcx, self.cx.typing_env()) { - return FfiSafe; + if let Some(inner_ty) = ty.boxed_ty() { + if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite = + get_type_sizedness(self.cx, inner_ty) + { + // discussion on declaration vs definition: + // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm + // of this `match *ty.kind()` block + if matches!(self.mode, CItemKind::Definition) { + return FfiSafe; + } else { + let inner_res = self.check_type_for_ffi(acc, inner_ty); + return match inner_res { + FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, + wrapped: Box::new(inner_res), + help: None, + }, + _ => inner_res, + }; + } } else { + let help = match inner_ty.kind() { + ty::Str => Some(fluent::lint_improper_ctypes_str_help), + ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help), + ty::Adt(def, _) + if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) + && matches!( + tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + && !acc.base_ty.is_mutable_ptr() => + { + Some(fluent::lint_improper_ctypes_cstr_help) + } + _ => None, + }; return FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_box, - help: None, + reason: fluent::lint_improper_ctypes_unsized_box, + help, }; } } @@ -1196,15 +1330,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Some(fluent::lint_improper_ctypes_tuple_help), }, - ty::RawPtr(ty, _) | ty::Ref(_, ty, _) - if { - matches!(self.mode, CItemKind::Definition) - && ty.is_sized(self.cx.tcx, self.cx.typing_env()) - } => - { - FfiSafe - } - ty::RawPtr(ty, _) if match ty.kind() { ty::Tuple(tuple) => tuple.is_empty(), @@ -1214,7 +1339,70 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty), + ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => { + if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite = + get_type_sizedness(self.cx, inner_ty) + { + // there's a nuance on what this lint should do for + // function definitions (`extern "C" fn fn_name(...) {...}`) + // versus declarations (`unsafe extern "C" {fn fn_name(...);}`). + // This is touched upon in https://github.com/rust-lang/rust/issues/66220 + // and https://github.com/rust-lang/rust/pull/72700 + // + // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer + // (which has a stable layout) but points to FFI-unsafe type, is it safe? + // On one hand, the function's ABI will match that of a similar C-declared function API, + // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful. + // In this code, the opinion on is split between function declarations and function definitions, + // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type. + // For declarations, we see this as unsafe, but for definitions, we see this as safe. + // + // For extern function declarations, the actual definition of the function is written somewhere else, + // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side) + // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, + // and having the full type information is necessary to compile the function. + if matches!(self.mode, CItemKind::Definition) { + return FfiSafe; + } else if matches!(ty.kind(), ty::RawPtr(..)) + && matches!(inner_ty.kind(), ty::Tuple(tuple) if tuple.is_empty()) + { + FfiSafe + } else { + let inner_res = self.check_type_for_ffi(acc, inner_ty); + return match inner_res { + FfiSafe => inner_res, + _ => FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, + wrapped: Box::new(inner_res), + help: None, + }, + }; + } + } else { + let help = match inner_ty.kind() { + ty::Str => Some(fluent::lint_improper_ctypes_str_help), + ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help), + ty::Adt(def, _) + if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) + && matches!( + tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + && !acc.base_ty.is_mutable_ptr() => + { + Some(fluent::lint_improper_ctypes_cstr_help) + } + _ => None, + }; + let reason = match ty.kind() { + ty::RawPtr(..) => fluent::lint_improper_ctypes_unsized_ptr, + ty::Ref(..) => fluent::lint_improper_ctypes_unsized_ref, + _ => unreachable!(), + }; + FfiUnsafe { ty, reason, help } + } + } ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty), @@ -1232,7 +1420,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { for arg in sig.inputs() { match self.check_type_for_ffi(acc, *arg) { FfiSafe => {} - r => return r, + r => { + return FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }; + } } } @@ -1241,7 +1436,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiSafe; } - self.check_type_for_ffi(acc, ret_ty) + match self.check_type_for_ffi(acc, ret_ty) { + r @ (FfiSafe | FfiPhantom(_)) => r, + r => FfiUnsafeWrapper { + ty: ty.clone(), + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }, + } } ty::Foreign(..) => FfiSafe, @@ -1278,8 +1481,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { &mut self, ty: Ty<'tcx>, sp: Span, - note: DiagMessage, - help: Option<DiagMessage>, + mut reasons: Vec<ImproperCTypesLayer<'tcx>>, ) { let lint = match self.mode { CItemKind::Declaration => IMPROPER_CTYPES, @@ -1289,21 +1491,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { CItemKind::Declaration => "block", CItemKind::Definition => "fn", }; - let span_note = if let ty::Adt(def, _) = ty.kind() - && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) - { - Some(sp) - } else { - None - }; - self.cx.emit_span_lint(lint, sp, ImproperCTypes { - ty, - desc, - label: sp, - help, - note, - span_note, - }); + for reason in reasons.iter_mut() { + reason.span_note = if let ty::Adt(def, _) = reason.ty.kind() + && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) + { + Some(sp) + } else { + None + }; + } + + self.cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons }); } fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { @@ -1332,7 +1530,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { .visit_with(&mut ProhibitOpaqueTypes) .break_value() { - self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None); + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { + ty, + note: fluent::lint_improper_ctypes_opaque, + span_note: Some(sp), + help: None, + inner_ty: None, + }]); true } else { false @@ -1371,15 +1575,71 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match self.check_type_for_ffi(&mut acc, ty) { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { - self.emit_ffi_unsafe_type_lint( + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { ty, - sp, - fluent::lint_improper_ctypes_only_phantomdata, - None, - ); + note: fluent::lint_improper_ctypes_only_phantomdata, + span_note: None, // filled later + help: None, + inner_ty: None, + }]); } FfiResult::FfiUnsafe { ty, reason, help } => { - self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { + ty, + help, + note: reason, + span_note: None, // filled later + inner_ty: None, + }]); + } + ffir @ FfiResult::FfiUnsafeWrapper { .. } => { + let mut ffiresult_recursor = ControlFlow::Continue(&ffir); + let mut cimproper_layers: Vec<ImproperCTypesLayer<'tcx>> = vec![]; + + // this whole while block converts the arbitrarily-deep + // FfiResult stack to an ImproperCTypesLayer Vec + while let ControlFlow::Continue(ref ffir_rec) = ffiresult_recursor { + match ffir_rec { + FfiResult::FfiPhantom(ty) => { + if let Some(layer) = cimproper_layers.last_mut() { + layer.inner_ty = Some(ty.clone()); + } + cimproper_layers.push(ImproperCTypesLayer { + ty: ty.clone(), + inner_ty: None, + help: None, + note: fluent::lint_improper_ctypes_only_phantomdata, + span_note: None, // filled later + }); + ffiresult_recursor = ControlFlow::Break(()); + } + FfiResult::FfiUnsafe { ty, reason, help } + | FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => { + if let Some(layer) = cimproper_layers.last_mut() { + layer.inner_ty = Some(ty.clone()); + } + cimproper_layers.push(ImproperCTypesLayer { + ty: ty.clone(), + inner_ty: None, + help: help.clone(), + note: reason.clone(), + span_note: None, // filled later + }); + + if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec { + ffiresult_recursor = ControlFlow::Continue(wrapped.as_ref()); + } else { + ffiresult_recursor = ControlFlow::Break(()); + } + } + FfiResult::FfiSafe => { + bug!("malformed FfiResult stack: it should be unsafe all the way down") + } + }; + } + // should always have at least one type + let last_ty = cimproper_layers.last().unwrap().ty.clone(); + self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers); } } } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index b775cd37409..9cad5d98562 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens { self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), + Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index d2b7ae620e2..54e927df3c4 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3929,6 +3929,7 @@ declare_lint! { } declare_lint! { + #[allow(text_direction_codepoint_in_literal)] /// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that /// change the visual representation of text on screen in a way that does not correspond to /// their on memory representation. diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d04876d0bef..a3976c3dda1 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -276,7 +276,7 @@ rustc_queries! { } /// The root query triggering all analysis passes like typeck or borrowck. - query analysis(key: ()) -> Result<(), ErrorGuaranteed> { + query analysis(key: ()) { eval_always desc { "running analysis passes on this crate" } } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 3849cb72668..119a99e1bf7 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -23,7 +23,7 @@ use rustc_session::Session; use rustc_span::hygiene::{ ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextData, }; -use rustc_span::source_map::{SourceMap, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::{ BytePos, CachingSourceMapView, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span, SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, @@ -49,7 +49,7 @@ const SYMBOL_PREINTERNED: u8 = 2; /// previous compilation session. This data will eventually include the results /// of a few selected queries (like `typeck` and `mir_optimized`) and /// any side effects that have been emitted during a query. -pub struct OnDiskCache<'sess> { +pub struct OnDiskCache { // The complete cache data in serialized form. serialized_data: RwLock<Option<Mmap>>, @@ -57,7 +57,6 @@ pub struct OnDiskCache<'sess> { // session. current_side_effects: Lock<FxHashMap<DepNodeIndex, QuerySideEffects>>, - source_map: &'sess SourceMap, file_index_to_stable_id: FxHashMap<SourceFileIndex, EncodedSourceFileId>, // Caches that are populated lazily during decoding. @@ -151,12 +150,12 @@ impl EncodedSourceFileId { } } -impl<'sess> OnDiskCache<'sess> { +impl OnDiskCache { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. /// /// The serialized cache has some basic integrity checks, if those checks indicate that the /// on-disk data is corrupt, an error is returned. - pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Result<Self, ()> { + pub fn new(sess: &Session, data: Mmap, start_pos: usize) -> Result<Self, ()> { assert!(sess.opts.incremental.is_some()); let mut decoder = MemDecoder::new(&data, start_pos)?; @@ -175,7 +174,6 @@ impl<'sess> OnDiskCache<'sess> { serialized_data: RwLock::new(Some(data)), file_index_to_stable_id: footer.file_index_to_stable_id, file_index_to_file: Default::default(), - source_map: sess.source_map(), current_side_effects: Default::default(), query_result_index: footer.query_result_index.into_iter().collect(), prev_side_effects_index: footer.side_effects_index.into_iter().collect(), @@ -187,12 +185,11 @@ impl<'sess> OnDiskCache<'sess> { }) } - pub fn new_empty(source_map: &'sess SourceMap) -> Self { + pub fn new_empty() -> Self { Self { serialized_data: RwLock::new(None), file_index_to_stable_id: Default::default(), file_index_to_file: Default::default(), - source_map, current_side_effects: Default::default(), query_result_index: Default::default(), prev_side_effects_index: Default::default(), @@ -423,7 +420,7 @@ impl<'sess> OnDiskCache<'sess> { } fn with_decoder<'a, 'tcx, T, F: for<'s> FnOnce(&mut CacheDecoder<'s, 'tcx>) -> T>( - &'sess self, + &self, tcx: TyCtxt<'tcx>, pos: AbsoluteBytePos, f: F, @@ -436,7 +433,6 @@ impl<'sess> OnDiskCache<'sess> { tcx, opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()) .unwrap(), - source_map: self.source_map, file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, alloc_decoding_session: self.alloc_decoding_state.new_decoding_session(), @@ -457,7 +453,6 @@ impl<'sess> OnDiskCache<'sess> { pub struct CacheDecoder<'a, 'tcx> { tcx: TyCtxt<'tcx>, opaque: MemDecoder<'a>, - source_map: &'a SourceMap, file_index_to_file: &'a Lock<FxHashMap<SourceFileIndex, Lrc<SourceFile>>>, file_index_to_stable_id: &'a FxHashMap<SourceFileIndex, EncodedSourceFileId>, alloc_decoding_session: AllocDecodingSession<'a>, @@ -470,8 +465,7 @@ pub struct CacheDecoder<'a, 'tcx> { impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { #[inline] fn file_index_to_file(&self, index: SourceFileIndex) -> Lrc<SourceFile> { - let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, source_map, .. } = - *self; + let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, .. } = *self; Lrc::clone(file_index_to_file.borrow_mut().entry(index).or_insert_with(|| { let source_file_id = &file_index_to_stable_id[&index]; @@ -490,7 +484,8 @@ impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { self.tcx.import_source_files(source_file_cnum); } - source_map + tcx.sess + .source_map() .source_file_by_stable_id(source_file_id.stable_source_file_id) .expect("failed to lookup `SourceFile` in new context") })) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 20ba1b27c0e..c8675660e0f 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -66,7 +66,7 @@ pub struct QuerySystem<'tcx> { /// Do not access this directly. It is only meant to be used by /// `DepGraph::try_mark_green()` and the query infrastructure. /// This is `None` if we are not incremental compilation mode - pub on_disk_cache: Option<OnDiskCache<'tcx>>, + pub on_disk_cache: Option<OnDiskCache>, pub fns: QuerySystemFns<'tcx>, diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index fd807882e0f..604f1da26c6 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -1,11 +1,12 @@ //! Diagnostics related methods for `Ty`. -use std::borrow::Cow; use std::fmt::Write; use std::ops::ControlFlow; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display}; +use rustc_errors::{ + Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, pluralize, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind}; @@ -161,7 +162,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>( true } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] enum SuggestChangingConstraintsMessage<'a> { RestrictBoundFurther, RestrictType { ty: &'a str }, @@ -172,7 +173,7 @@ enum SuggestChangingConstraintsMessage<'a> { fn suggest_changing_unsized_bound( generics: &hir::Generics<'_>, - suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>, + suggestions: &mut Vec<(Span, String, String, SuggestChangingConstraintsMessage<'_>)>, param: &hir::GenericParam<'_>, def_id: Option<DefId>, ) { @@ -207,7 +208,8 @@ fn suggest_changing_unsized_bound( continue; } - let mut push_suggestion = |sp, msg| suggestions.push((sp, String::new(), msg)); + let mut push_suggestion = + |sp, msg| suggestions.push((sp, "Sized".to_string(), String::new(), msg)); if predicate.bounds.len() == unsized_bounds.len() { // All the bounds are unsized bounds, e.g. @@ -278,8 +280,25 @@ pub fn suggest_constraining_type_params<'a>( span_to_replace: Option<Span>, ) -> bool { let mut grouped = FxHashMap::default(); + let mut unstable_suggestion = false; param_names_and_constraints.for_each(|(param_name, constraint, def_id)| { - grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id)) + let stable = match def_id { + Some(def_id) => match tcx.lookup_stability(def_id) { + Some(s) => s.level.is_stable(), + None => true, + }, + None => true, + }; + if stable || tcx.sess.is_nightly_build() { + grouped.entry(param_name).or_insert(Vec::new()).push(( + constraint, + def_id, + if stable { "" } else { "unstable " }, + )); + if !stable { + unstable_suggestion = true; + } + } }); let mut applicability = Applicability::MachineApplicable; @@ -290,16 +309,21 @@ pub fn suggest_constraining_type_params<'a>( let Some(param) = param else { return false }; { - let mut sized_constraints = constraints.extract_if(|(_, def_id)| { + let mut sized_constraints = constraints.extract_if(|(_, def_id, _)| { def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized)) }); - if let Some((_, def_id)) = sized_constraints.next() { + if let Some((_, def_id, _)) = sized_constraints.next() { applicability = Applicability::MaybeIncorrect; err.span_label(param.span, "this type parameter needs to be `Sized`"); suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id); } } + let bound_message = if constraints.iter().any(|(_, def_id, _)| def_id.is_none()) { + SuggestChangingConstraintsMessage::RestrictBoundFurther + } else { + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name } + }; // in the scenario like impl has stricter requirements than trait, // we should not suggest restrict bound on the impl, here we double check @@ -312,15 +336,54 @@ pub fn suggest_constraining_type_params<'a>( .collect(); constraints - .retain(|(_, def_id)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def))); + .retain(|(_, def_id, _)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def))); if constraints.is_empty() { continue; } - let mut constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>(); + let mut constraint = constraints.iter().map(|&(c, _, _)| c).collect::<Vec<_>>(); constraint.sort(); constraint.dedup(); + let all_known = constraints.iter().all(|&(_, def_id, _)| def_id.is_some()); + let all_stable = constraints.iter().all(|&(_, _, stable)| stable.is_empty()); + let all_unstable = constraints.iter().all(|&(_, _, stable)| !stable.is_empty()); + let post = if all_stable || all_unstable { + // Don't redundantly say "trait `X`, trait `Y`", instead "traits `X` and `Y`" + let mut trait_names = constraints + .iter() + .map(|&(c, def_id, _)| match def_id { + None => format!("`{c}`"), + Some(def_id) => format!("`{}`", tcx.item_name(def_id)), + }) + .collect::<Vec<_>>(); + trait_names.sort(); + trait_names.dedup(); + let n = trait_names.len(); + let stable = if all_stable { "" } else { "unstable " }; + let trait_ = if all_known { format!("trait{}", pluralize!(n)) } else { String::new() }; + format!("{stable}{trait_}{}", match &trait_names[..] { + [t] => format!(" {t}"), + [ts @ .., last] => format!(" {} and {last}", ts.join(", ")), + [] => return false, + },) + } else { + // We're more explicit when there's a mix of stable and unstable traits. + let mut trait_names = constraints + .iter() + .map(|&(c, def_id, stable)| match def_id { + None => format!("`{c}`"), + Some(def_id) => format!("{stable}trait `{}`", tcx.item_name(def_id)), + }) + .collect::<Vec<_>>(); + trait_names.sort(); + trait_names.dedup(); + match &trait_names[..] { + [t] => t.to_string(), + [ts @ .., last] => format!("{} and {last}", ts.join(", ")), + [] => return false, + } + }; let constraint = constraint.join(" + "); let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| { let suggestion = if span_to_replace.is_some() { @@ -333,13 +396,11 @@ pub fn suggest_constraining_type_params<'a>( format!(" {constraint}") }; - use SuggestChangingConstraintsMessage::RestrictBoundFurther; - if let Some(open_paren_sp) = open_paren_sp { - suggestions.push((open_paren_sp, "(".to_string(), RestrictBoundFurther)); - suggestions.push((span, format!("){suggestion}"), RestrictBoundFurther)); + suggestions.push((open_paren_sp, post.clone(), "(".to_string(), bound_message)); + suggestions.push((span, post.clone(), format!("){suggestion}"), bound_message)); } else { - suggestions.push((span, suggestion, RestrictBoundFurther)); + suggestions.push((span, post.clone(), suggestion, bound_message)); } }; @@ -397,7 +458,8 @@ pub fn suggest_constraining_type_params<'a>( // - insert: `, X: Bar` suggestions.push(( generics.tail_span_for_predicate_suggestion(), - constraints.iter().fold(String::new(), |mut string, &(constraint, _)| { + post, + constraints.iter().fold(String::new(), |mut string, &(constraint, _, _)| { write!(string, ", {param_name}: {constraint}").unwrap(); string }), @@ -426,6 +488,7 @@ pub fn suggest_constraining_type_params<'a>( // default (`<T=Foo>`), so we suggest adding `where T: Bar`. suggestions.push(( generics.tail_span_for_predicate_suggestion(), + post, format!("{where_prefix} {param_name}: {constraint}"), SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, )); @@ -439,6 +502,7 @@ pub fn suggest_constraining_type_params<'a>( if let Some(colon_span) = param.colon_span { suggestions.push(( colon_span.shrink_to_hi(), + post, format!(" {constraint}"), SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, )); @@ -451,6 +515,7 @@ pub fn suggest_constraining_type_params<'a>( // - help: consider restricting this type parameter with `T: Foo` suggestions.push(( param.span.shrink_to_hi(), + post, format!(": {constraint}"), SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, )); @@ -459,39 +524,46 @@ pub fn suggest_constraining_type_params<'a>( // FIXME: remove the suggestions that are from derive, as the span is not correct suggestions = suggestions .into_iter() - .filter(|(span, _, _)| !span.in_derive_expansion()) + .filter(|(span, _, _, _)| !span.in_derive_expansion()) .collect::<Vec<_>>(); - + let suggested = !suggestions.is_empty(); if suggestions.len() == 1 { - let (span, suggestion, msg) = suggestions.pop().unwrap(); + let (span, post, suggestion, msg) = suggestions.pop().unwrap(); let msg = match msg { SuggestChangingConstraintsMessage::RestrictBoundFurther => { - Cow::from("consider further restricting this bound") + format!("consider further restricting this bound") + } + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } + | SuggestChangingConstraintsMessage::RestrictType { ty } + if ty.starts_with("impl ") => + { + format!("consider restricting opaque type `{ty}` with {post}") } SuggestChangingConstraintsMessage::RestrictType { ty } => { - Cow::from(format!("consider restricting type parameter `{ty}`")) + format!("consider restricting type parameter `{ty}` with {post}") } SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => { - Cow::from(format!("consider further restricting type parameter `{ty}`")) + format!("consider further restricting type parameter `{ty}` with {post}") } SuggestChangingConstraintsMessage::RemoveMaybeUnsized => { - Cow::from("consider removing the `?Sized` bound to make the type parameter `Sized`") + format!("consider removing the `?Sized` bound to make the type parameter `Sized`") } SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => { - Cow::from("consider replacing `?Sized` with `Sized`") + format!("consider replacing `?Sized` with `Sized`") } }; err.span_suggestion_verbose(span, msg, suggestion, applicability); } else if suggestions.len() > 1 { + let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" }; err.multipart_suggestion_verbose( - "consider restricting type parameters", - suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(), + format!("consider restricting type parameters{post}"), + suggestions.into_iter().map(|(span, _, suggestion, _)| (span, suggestion)).collect(), applicability, ); } - true + suggested } /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 561229cf725..f844e8fbe03 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -2,19 +2,186 @@ use std::borrow::Cow; use std::cell::RefCell; +use std::ffi::OsString; +use std::path::PathBuf; use std::sync::OnceLock; use std::{io, ops, str}; use regex::Regex; -use rustc_graphviz as dot; +use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; -use rustc_middle::mir::{self, BasicBlock, Body, Location, graphviz_safe_def_name}; +use rustc_middle::mir::{ + self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name, + traversal, +}; +use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_span::symbol::{Symbol, sym}; +use tracing::debug; +use {rustc_ast as ast, rustc_graphviz as dot}; use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor}; +use crate::errors::{ + DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, +}; + +/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via +/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are +/// the same. +pub(super) fn write_graphviz_results<'tcx, A>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + results: &mut Results<'tcx, A>, + pass_name: Option<&'static str>, +) -> std::io::Result<()> +where + A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, +{ + use std::fs; + use std::io::Write; + + let def_id = body.source.def_id(); + let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { + // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` + return Ok(()); + }; + + let file = try { + match attrs.output_path(A::NAME) { + Some(path) => { + debug!("printing dataflow results for {:?} to {}", def_id, path.display()); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + fs::File::create_buffered(&path)? + } + + None if dump_enabled(tcx, A::NAME, def_id) => { + create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? + } + + _ => return Ok(()), + } + }; + let mut file = match file { + Ok(f) => f, + Err(e) => return Err(e), + }; + + let style = match attrs.formatter { + Some(sym::two_phase) => OutputStyle::BeforeAndAfter, + _ => OutputStyle::AfterOnly, + }; + + let mut buf = Vec::new(); + + let graphviz = Formatter::new(body, results, style); + let mut render_opts = + vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())]; + if tcx.sess.opts.unstable_opts.graphviz_dark_mode { + render_opts.push(dot::RenderOption::DarkTheme); + } + let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)); + + let lhs = try { + r?; + file.write_all(&buf)?; + }; + + lhs +} + +#[derive(Default)] +struct RustcMirAttrs { + basename_and_suffix: Option<PathBuf>, + formatter: Option<Symbol>, +} + +impl RustcMirAttrs { + fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> { + let mut result = Ok(()); + let mut ret = RustcMirAttrs::default(); + + let rustc_mir_attrs = tcx + .get_attrs(def_id, sym::rustc_mir) + .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); + + for attr in rustc_mir_attrs { + let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) { + Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| { + let path = PathBuf::from(s.to_string()); + match path.file_name() { + Some(_) => Ok(path), + None => { + tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); + Err(()) + } + } + }) + } else if attr.has_name(sym::borrowck_graphviz_format) { + Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s { + sym::gen_kill | sym::two_phase => Ok(s), + _ => { + tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); + Err(()) + } + }) + } else { + Ok(()) + }; + + result = result.and(attr_result); + } + + result.map(|()| ret) + } + + fn set_field<T>( + field: &mut Option<T>, + tcx: TyCtxt<'_>, + attr: &ast::MetaItemInner, + mapper: impl FnOnce(Symbol) -> Result<T, ()>, + ) -> Result<(), ()> { + if field.is_some() { + tcx.dcx() + .emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() }); + + return Err(()); + } + + if let Some(s) = attr.value_str() { + *field = Some(mapper(s)?); + Ok(()) + } else { + tcx.dcx() + .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() }); + Err(()) + } + } + + /// Returns the path where dataflow results should be written, or `None` + /// `borrowck_graphviz_postflow` was not specified. + /// + /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`: + /// + /// "path/suffix.dot" -> "path/analysis_name_suffix.dot" + fn output_path(&self, analysis_name: &str) -> Option<PathBuf> { + let mut ret = self.basename_and_suffix.as_ref().cloned()?; + let suffix = ret.file_name().unwrap(); // Checked when parsing attrs + + let mut file_name: OsString = analysis_name.into(); + file_name.push("_"); + file_name.push(suffix); + ret.set_file_name(file_name); + + Some(ret) + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(crate) enum OutputStyle { +enum OutputStyle { AfterOnly, BeforeAndAfter, } @@ -28,7 +195,7 @@ impl OutputStyle { } } -pub(crate) struct Formatter<'mir, 'tcx, A> +struct Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { @@ -45,12 +212,12 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { - pub(crate) fn new( + fn new( body: &'mir Body<'tcx>, results: &'mir mut Results<'tcx, A>, style: OutputStyle, ) -> Self { - let reachable = mir::traversal::reachable_as_bitset(body); + let reachable = traversal::reachable_as_bitset(body); Formatter { cursor: results.as_results_cursor(body).into(), style, reachable } } @@ -61,7 +228,7 @@ where /// A pair of a basic block and an index into that basic blocks `successors`. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(crate) struct CfgEdge { +struct CfgEdge { source: BasicBlock, index: usize, } @@ -520,7 +687,7 @@ struct StateDiffCollector<D> { impl<D> StateDiffCollector<D> { fn run<'tcx, A>( - body: &mir::Body<'tcx>, + body: &Body<'tcx>, block: BasicBlock, results: &mut Results<'tcx, A>, style: OutputStyle, diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index b9407882ec5..13384d6f285 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -42,7 +42,7 @@ use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, Terminator use rustc_middle::ty::TyCtxt; use tracing::error; -use self::results::write_graphviz_results; +use self::graphviz::write_graphviz_results; use super::fmt::DebugWithContext; mod cursor; diff --git a/compiler/rustc_mir_dataflow/src/framework/results.rs b/compiler/rustc_mir_dataflow/src/framework/results.rs index c4321c454e6..a7dbd99b8ab 100644 --- a/compiler/rustc_mir_dataflow/src/framework/results.rs +++ b/compiler/rustc_mir_dataflow/src/framework/results.rs @@ -1,22 +1,9 @@ //! Dataflow analysis results. -use std::ffi::OsString; -use std::path::PathBuf; - -use rustc_hir::def_id::DefId; use rustc_index::IndexVec; -use rustc_middle::mir::{self, BasicBlock, create_dump_file, dump_enabled, traversal}; -use rustc_middle::ty::TyCtxt; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_span::symbol::{Symbol, sym}; -use tracing::debug; -use {rustc_ast as ast, rustc_graphviz as dot}; +use rustc_middle::mir::{BasicBlock, Body, traversal}; -use super::fmt::DebugWithContext; -use super::{Analysis, ResultsCursor, ResultsVisitor, graphviz, visit_results}; -use crate::errors::{ - DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, -}; +use super::{Analysis, ResultsCursor, ResultsVisitor, visit_results}; use crate::framework::cursor::ResultsHandle; pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>; @@ -41,16 +28,13 @@ where /// `Results` is also used outside the cursor. pub fn as_results_cursor<'mir>( &'mir mut self, - body: &'mir mir::Body<'tcx>, + body: &'mir Body<'tcx>, ) -> ResultsCursor<'mir, 'tcx, A> { ResultsCursor::new(body, ResultsHandle::BorrowedMut(self)) } /// Creates a `ResultsCursor` that takes ownership of the `Results`. - pub fn into_results_cursor<'mir>( - self, - body: &'mir mir::Body<'tcx>, - ) -> ResultsCursor<'mir, 'tcx, A> { + pub fn into_results_cursor<'mir>(self, body: &'mir Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> { ResultsCursor::new(body, ResultsHandle::Owned(self)) } @@ -61,7 +45,7 @@ where pub fn visit_with<'mir>( &mut self, - body: &'mir mir::Body<'tcx>, + body: &'mir Body<'tcx>, blocks: impl IntoIterator<Item = BasicBlock>, vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, ) { @@ -70,166 +54,10 @@ where pub fn visit_reachable_with<'mir>( &mut self, - body: &'mir mir::Body<'tcx>, + body: &'mir Body<'tcx>, vis: &mut impl ResultsVisitor<'mir, 'tcx, A>, ) { let blocks = traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) } } - -// Graphviz - -/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via -/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are -/// the same. -pub(super) fn write_graphviz_results<'tcx, A>( - tcx: TyCtxt<'tcx>, - body: &mir::Body<'tcx>, - results: &mut Results<'tcx, A>, - pass_name: Option<&'static str>, -) -> std::io::Result<()> -where - A: Analysis<'tcx>, - A::Domain: DebugWithContext<A>, -{ - use std::fs; - use std::io::Write; - - let def_id = body.source.def_id(); - let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { - // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` - return Ok(()); - }; - - let file = try { - match attrs.output_path(A::NAME) { - Some(path) => { - debug!("printing dataflow results for {:?} to {}", def_id, path.display()); - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::File::create_buffered(&path)? - } - - None if dump_enabled(tcx, A::NAME, def_id) => { - create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? - } - - _ => return Ok(()), - } - }; - let mut file = match file { - Ok(f) => f, - Err(e) => return Err(e), - }; - - let style = match attrs.formatter { - Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter, - _ => graphviz::OutputStyle::AfterOnly, - }; - - let mut buf = Vec::new(); - - let graphviz = graphviz::Formatter::new(body, results, style); - let mut render_opts = - vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())]; - if tcx.sess.opts.unstable_opts.graphviz_dark_mode { - render_opts.push(dot::RenderOption::DarkTheme); - } - let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)); - - let lhs = try { - r?; - file.write_all(&buf)?; - }; - - lhs -} - -#[derive(Default)] -struct RustcMirAttrs { - basename_and_suffix: Option<PathBuf>, - formatter: Option<Symbol>, -} - -impl RustcMirAttrs { - fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> { - let mut result = Ok(()); - let mut ret = RustcMirAttrs::default(); - - let rustc_mir_attrs = tcx - .get_attrs(def_id, sym::rustc_mir) - .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); - - for attr in rustc_mir_attrs { - let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) { - Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| { - let path = PathBuf::from(s.to_string()); - match path.file_name() { - Some(_) => Ok(path), - None => { - tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); - Err(()) - } - } - }) - } else if attr.has_name(sym::borrowck_graphviz_format) { - Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s { - sym::gen_kill | sym::two_phase => Ok(s), - _ => { - tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); - Err(()) - } - }) - } else { - Ok(()) - }; - - result = result.and(attr_result); - } - - result.map(|()| ret) - } - - fn set_field<T>( - field: &mut Option<T>, - tcx: TyCtxt<'_>, - attr: &ast::MetaItemInner, - mapper: impl FnOnce(Symbol) -> Result<T, ()>, - ) -> Result<(), ()> { - if field.is_some() { - tcx.dcx() - .emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() }); - - return Err(()); - } - - if let Some(s) = attr.value_str() { - *field = Some(mapper(s)?); - Ok(()) - } else { - tcx.dcx() - .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() }); - Err(()) - } - } - - /// Returns the path where dataflow results should be written, or `None` - /// `borrowck_graphviz_postflow` was not specified. - /// - /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`: - /// - /// "path/suffix.dot" -> "path/analysis_name_suffix.dot" - fn output_path(&self, analysis_name: &str) -> Option<PathBuf> { - let mut ret = self.basename_and_suffix.as_ref().cloned()?; - let suffix = ret.file_name().unwrap(); // Checked when parsing attrs - - let mut file_name: OsString = analysis_name.into(); - file_name.push("_"); - file_name.push(suffix); - ret.set_file_name(file_name); - - Some(ret) - } -} diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index b9e194b00c5..d69a8019c8d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -1,7 +1,3 @@ -//! Dataflow analyses are built upon some interpretation of the -//! bitvectors attached to each basic block, represented via a -//! zero-sized structure. - mod borrowed_locals; mod initialized; mod liveness; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8d16d44b0a2..3a9e9b480ec 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2631,7 +2631,7 @@ impl<'a> Parser<'a> { }; self.bump(); // Eat `let` token let lo = self.prev_token.span; - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -2778,7 +2778,7 @@ impl<'a> Parser<'a> { }; // Try to parse the pattern `for ($PAT) in $EXPR`. let pat = match ( - self.parse_pat_allow_top_alt( + self.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3241,7 +3241,7 @@ impl<'a> Parser<'a> { // then we should recover. let mut snapshot = this.create_snapshot_for_diagnostic(); let pattern_follows = snapshot - .parse_pat_allow_top_alt( + .parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3315,43 +3315,37 @@ impl<'a> Parser<'a> { fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> { if self.token == token::OpenDelim(Delimiter::Parenthesis) { - // Detect and recover from `($pat if $cond) => $arm`. let left = self.token.span; - match self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, CommaRecoveryMode::EitherTupleOrPipe, - ) { - Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)), - Err(err) - if let prev_sp = self.prev_token.span - && let true = self.eat_keyword(kw::If) => - { - // We know for certain we've found `($pat if` so far. - let mut cond = match self.parse_match_guard_condition() { - Ok(cond) => cond, - Err(cond_err) => { - cond_err.cancel(); - return Err(err); - } - }; - err.cancel(); - CondChecker::new(self).visit_expr(&mut cond); - self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]); - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; - let right = self.prev_token.span; - self.dcx().emit_err(errors::ParenthesesInMatchPat { - span: vec![left, right], - sugg: errors::ParenthesesInMatchPatSugg { left, right }, - }); - Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond))) - } - Err(err) => Err(err), + )?; + if let ast::PatKind::Paren(subpat) = &pat.kind + && let ast::PatKind::Guard(..) = &subpat.kind + { + // Detect and recover from `($pat if $cond) => $arm`. + // FIXME(guard_patterns): convert this to a normal guard instead + let span = pat.span; + let ast::PatKind::Paren(subpat) = pat.into_inner().kind else { unreachable!() }; + let ast::PatKind::Guard(_, mut cond) = subpat.into_inner().kind else { + unreachable!() + }; + self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); + CondChecker::new(self).visit_expr(&mut cond); + let right = self.prev_token.span; + self.dcx().emit_err(errors::ParenthesesInMatchPat { + span: vec![left, right], + sugg: errors::ParenthesesInMatchPatSugg { left, right }, + }); + Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond))) + } else { + Ok((pat, self.parse_match_arm_guard()?)) } } else { // Regular parser flow: - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 8fb6f85d0dd..752a52b382b 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -174,7 +174,7 @@ impl<'a> Parser<'a> { NonterminalKind::Pat(pat_kind) => { NtPat(self.collect_tokens_no_attrs(|this| match pat_kind { PatParam { .. } => this.parse_pat_no_top_alt(None, None), - PatWithOr => this.parse_pat_allow_top_alt( + PatWithOr => this.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index e08b925f008..4cda887a02b 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -99,9 +99,34 @@ pub enum PatternLocation { impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns - /// at the top level. Used when parsing the parameters of lambda expressions, - /// functions, function pointers, and `pat` macro fragments. + /// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level. + /// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor + /// `PatternNoTopAlt` (see below) are used. + pub fn parse_pat_allow_top_guard( + &mut self, + expected: Option<Expected>, + rc: RecoverComma, + ra: RecoverColon, + rt: CommaRecoveryMode, + ) -> PResult<'a, P<Pat>> { + let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?; + + if self.eat_keyword(kw::If) { + let cond = self.parse_expr()?; + // Feature-gate guard patterns + self.psess.gated_spans.gate(sym::guard_patterns, cond.span); + let span = pat.span.to(cond.span); + Ok(self.mk_pat(span, PatKind::Guard(pat, cond))) + } else { + Ok(pat) + } + } + + /// Parses a pattern. + /// + /// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns + /// or guard patterns at the top level. Used when parsing the parameters of lambda + /// expressions, functions, function pointers, and `pat_param` macro fragments. pub fn parse_pat_no_top_alt( &mut self, expected: Option<Expected>, @@ -112,25 +137,26 @@ impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. - /// Used for parsing patterns in all cases when `pat<no_top_alt>` is not used. + /// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not + /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until + /// the next edition) and `let`, `if let`, and `while let` expressions. /// /// Note that after the FCP in <https://github.com/rust-lang/rust/issues/81415>, /// a leading vert is allowed in nested or-patterns, too. This allows us to /// simplify the grammar somewhat. - pub fn parse_pat_allow_top_alt( + pub fn parse_pat_no_top_guard( &mut self, expected: Option<Expected>, rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, ) -> PResult<'a, P<Pat>> { - self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) + self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = /// recovered). - fn parse_pat_allow_top_alt_inner( + fn parse_pat_no_top_guard_inner( &mut self, expected: Option<Expected>, rc: RecoverComma, @@ -231,7 +257,7 @@ impl<'a> Parser<'a> { // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level // or-patterns so that we can detect when a user tries to use it. This allows us to print a // better error message. - let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner( + let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner( expected, rc, RecoverColon::No, @@ -696,7 +722,7 @@ impl<'a> Parser<'a> { } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { // Parse `[pat, pat,...]` as a slice pattern. let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -944,7 +970,7 @@ impl<'a> Parser<'a> { let open_paren = self.token.span; let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1361,7 +1387,7 @@ impl<'a> Parser<'a> { path: Path, ) -> PResult<'a, PatKind> { let (fields, _) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1396,7 +1422,7 @@ impl<'a> Parser<'a> { self.parse_builtin(|self_, _lo, ident| { Ok(match ident.name { // builtin#deref(PAT) - sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt( + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -1671,7 +1697,7 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form `fieldname: pat`. let fieldname = self.parse_field_name()?; self.bump(); - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 2f19a9b6b20..6a7029a8f1c 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -469,7 +469,7 @@ impl<'a> Parser<'a> { PathStyle::Pat if let Ok(_) = self .parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index b8f66a2b2ec..f2a37307cee 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -555,6 +555,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Slice, Rest, Never, + Guard, Paren, MacCall, Err diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index a1917fed4d9..d2bb0b3f277 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -198,12 +198,12 @@ trait QueryConfigRestored<'tcx> { -> Self::RestoredValue; } -pub fn query_system<'tcx>( +pub fn query_system<'a>( local_providers: Providers, extern_providers: ExternProviders, - on_disk_cache: Option<OnDiskCache<'tcx>>, + on_disk_cache: Option<OnDiskCache>, incremental: bool, -) -> QuerySystem<'tcx> { +) -> QuerySystem<'a> { QuerySystem { states: Default::default(), arenas: Default::default(), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 7db3b7b7d9d..993d111466b 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -19,7 +19,6 @@ use rustc_errors::emitter::{ DynEmitter, HumanEmitter, HumanReadableErrorType, OutputTheme, stderr_destination, }; use rustc_errors::json::JsonEmitter; -use rustc_errors::registry::Registry; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle, @@ -276,11 +275,11 @@ impl Session { } /// Invoked all the way at the end to finish off diagnostics printing. - pub fn finish_diagnostics(&self, registry: &Registry) -> Option<ErrorGuaranteed> { + pub fn finish_diagnostics(&self) -> Option<ErrorGuaranteed> { let mut guar = None; guar = guar.or(self.check_miri_unleashed_features()); guar = guar.or(self.dcx().emit_stashed_diagnostics()); - self.dcx().print_error_count(registry); + self.dcx().print_error_count(); if self.opts.json_future_incompat { self.dcx().emit_future_breakage_report(); } @@ -880,7 +879,6 @@ impl Session { #[allow(rustc::bad_opt_access)] fn default_emitter( sopts: &config::Options, - registry: rustc_errors::registry::Registry, source_map: Lrc<SourceMap>, bundle: Option<Lrc<FluentBundle>>, fallback_bundle: LazyFallbackBundle, @@ -943,7 +941,6 @@ fn default_emitter( json_rendered, color_config, ) - .registry(Some(registry)) .fluent_bundle(bundle) .ui_testing(sopts.unstable_opts.ui_testing) .ignored_directories_in_source_blocks( @@ -999,11 +996,11 @@ pub fn build_session( sopts.unstable_opts.translate_directionality_markers, ); let source_map = rustc_span::source_map::get_source_map().unwrap(); - let emitter = - default_emitter(&sopts, registry, Lrc::clone(&source_map), bundle, fallback_bundle); + let emitter = default_emitter(&sopts, Lrc::clone(&source_map), bundle, fallback_bundle); - let mut dcx = - DiagCtxt::new(emitter).with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)); + let mut dcx = DiagCtxt::new(emitter) + .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)) + .with_registry(registry); if let Some(ice_file) = ice_file { dcx = dcx.with_ice_file(ice_file); } diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index d4b034ea219..614c9169d66 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -342,8 +342,9 @@ macro_rules! run_driver { /// Runs the compiler against given target and tests it with `test_function` pub fn run(&mut self) -> Result<C, CompilerError<B>> { - let compiler_result = rustc_driver::catch_fatal_errors(|| { - RunCompiler::new(&self.args.clone(), self).run() + let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> { + RunCompiler::new(&self.args.clone(), self).run(); + Ok(()) }); match (compiler_result, self.result.take()) { (Ok(Ok(())), Some(ControlFlow::Continue(value))) => Ok(value), diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 57c645bfaba..958d4f0c251 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -51,6 +51,7 @@ pub mod source_map; use source_map::{SourceMap, SourceMapInputs}; pub use self::caching_source_map_view::CachingSourceMapView; +use crate::fatal_error::FatalError; pub mod edition; use edition::Edition; @@ -2614,6 +2615,10 @@ impl ErrorGuaranteed { pub fn unchecked_error_guaranteed() -> Self { ErrorGuaranteed(()) } + + pub fn raise_fatal(self) -> ! { + FatalError.raise() + } } impl<E: rustc_serialize::Encoder> Encodable<E> for ErrorGuaranteed { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a7dc20a874b..818d4afffc6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -999,6 +999,7 @@ symbols! { global_registration, globs, gt, + guard_patterns, half_open_range_patterns, half_open_range_patterns_in_slices, hash, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 8ba7969207e..f856a8d7abb 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -52,7 +52,9 @@ use std::{cmp, fmt, iter}; use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize}; +use rustc_errors::{ + Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -67,6 +69,7 @@ use rustc_middle::ty::{ self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{BytePos, DesugaringKind, Pos, Span, sym}; use tracing::{debug, instrument}; @@ -211,7 +214,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } /// Adds a note if the types come from similarly named crates - fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) { + fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool { + // FIXME(estebank): unify with `report_similar_impl_candidates`. The message is similar, + // even if the logic needed to detect the case is very different. use hir::def_id::CrateNum; use rustc_hir::definitions::DisambiguatedDefPathData; use ty::GenericArg; @@ -285,7 +290,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId| { + let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId, ty: &str| -> bool { // Only report definitions from different crates. If both definitions // are from a local module we could have false positives, e.g. // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; @@ -297,24 +302,112 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // We compare strings because DefPath can be different // for imported and non-imported crates + let expected_str = self.tcx.def_path_str(did1); + let found_str = self.tcx.def_path_str(did2); + let Ok(expected_abs) = abs_path(did1) else { return false }; + let Ok(found_abs) = abs_path(did2) else { return false }; let same_path = || -> Result<_, PrintError> { - Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2) - || abs_path(did1)? == abs_path(did2)?) + Ok(expected_str == found_str || expected_abs == found_abs) + }; + // We want to use as unique a type path as possible. If both types are "locally + // known" by the same name, we use the "absolute path" which uses the original + // crate name instead. + let (expected, found) = if expected_str == found_str { + (expected_abs.join("::"), found_abs.join("::")) + } else { + (expected_str.clone(), found_str.clone()) }; if same_path().unwrap_or(false) { - let crate_name = self.tcx.crate_name(did1.krate); - let msg = if did1.is_local() || did2.is_local() { + // We've displayed "expected `a::b`, found `a::b`". We add context to + // differentiate the different cases where that might happen. + let expected_crate_name = self.tcx.crate_name(did1.krate); + let found_crate_name = self.tcx.crate_name(did2.krate); + let same_crate = expected_crate_name == found_crate_name; + let expected_sp = self.tcx.def_span(did1); + let found_sp = self.tcx.def_span(did2); + + let both_direct_dependencies = if !did1.is_local() + && !did2.is_local() + && let Some(data1) = self.tcx.extern_crate(did1.krate) + && let Some(data2) = self.tcx.extern_crate(did2.krate) + && data1.dependency_of == LOCAL_CRATE + && data2.dependency_of == LOCAL_CRATE + { + // If both crates are directly depended on, we don't want to mention that + // in the final message, as it is redundant wording. + // We skip the case of semver trick, where one version of the local crate + // depends on another version of itself by checking that both crates at play + // are not the current one. + true + } else { + false + }; + + let mut span: MultiSpan = vec![expected_sp, found_sp].into(); + span.push_span_label( + self.tcx.def_span(did1), + format!("this is the expected {ty} `{expected}`"), + ); + span.push_span_label( + self.tcx.def_span(did2), + format!("this is the found {ty} `{found}`"), + ); + for def_id in [did1, did2] { + let crate_name = self.tcx.crate_name(def_id.krate); + if !def_id.is_local() + && let Some(data) = self.tcx.extern_crate(def_id.krate) + { + let descr = if same_crate { + "one version of".to_string() + } else { + format!("one {ty} comes from") + }; + let dependency = if both_direct_dependencies { + if let rustc_session::cstore::ExternCrateSource::Extern(def_id) = + data.src + && let Some(name) = self.tcx.opt_item_name(def_id) + { + format!(", which is renamed locally to `{name}`") + } else { + String::new() + } + } else if data.dependency_of == LOCAL_CRATE { + ", as a direct dependency of the current crate".to_string() + } else { + let dep = self.tcx.crate_name(data.dependency_of); + format!(", as a dependency of crate `{dep}`") + }; + span.push_span_label( + data.span, + format!("{descr} crate `{crate_name}` used here{dependency}"), + ); + } + } + let msg = if (did1.is_local() || did2.is_local()) && same_crate { + format!( + "the crate `{expected_crate_name}` is compiled multiple times, \ + possibly with different configurations", + ) + } else if same_crate { format!( - "the crate `{crate_name}` is compiled multiple times, possibly with different configurations" + "two different versions of crate `{expected_crate_name}` are being \ + used; two types coming from two different versions of the same crate \ + are different types even if they look the same", ) } else { format!( - "perhaps two different versions of crate `{crate_name}` are being used?" + "two types coming from two different crates are different types even \ + if they look the same", ) }; - err.note(msg); + err.span_note(span, msg); + if same_crate { + err.help("you can use `cargo tree` to explore your dependency tree"); + } + return true; } } + false }; match terr { TypeError::Sorts(ref exp_found) => { @@ -323,14 +416,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) = (exp_found.expected.kind(), exp_found.found.kind()) { - report_path_match(err, exp_adt.did(), found_adt.did()); + return report_path_match(err, exp_adt.did(), found_adt.did(), "type"); } } TypeError::Traits(ref exp_found) => { - report_path_match(err, exp_found.expected, exp_found.found); + return report_path_match(err, exp_found.expected, exp_found.found, "trait"); } _ => (), // FIXME(#22750) handle traits and stuff } + false } fn note_error_origin( @@ -1409,6 +1503,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label_or_note(span, terr.to_string(self.tcx)); } + if self.check_and_note_conflicting_crates(diag, terr) { + return; + } + if let Some((expected, found, path)) = expected_found { let (expected_label, found_label, exp_found) = match exp_found { Mismatch::Variable(ef) => ( @@ -1470,15 +1568,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| { let name = shadow.sort_string(self.tcx); diag.note(format!( - "{prim} and {name} have similar names, but are actually distinct types" + "`{prim}` and {name} have similar names, but are actually distinct types" + )); + diag.note(format!( + "one `{prim}` is a primitive defined by the language", )); - diag.note(format!("{prim} is a primitive defined by the language")); let def_span = self.tcx.def_span(defid); let msg = if defid.is_local() { - format!("{name} is defined in the current crate") + format!("the other {name} is defined in the current crate") } else { let crate_name = self.tcx.crate_name(defid.krate); - format!("{name} is defined in crate `{crate_name}`") + format!("the other {name} is defined in crate `{crate_name}`") }; diag.span_note(def_span, msg); }; @@ -1666,8 +1766,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - self.check_and_note_conflicting_crates(diag, terr); - self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id()); if let Some(exp_found) = exp_found && let exp_found = TypeError::Sorts(exp_found) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 90b18253629..4fb02f60943 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1745,9 +1745,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; ( data.span, - format!( - "one version of crate `{crate_name}` is used here, as a {dependency}" - ), + format!("one version of crate `{crate_name}` used here, as a {dependency}"), ) }) { |
