diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 62995dfd2e2f0..a5f1cbc96daa7 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -19,6 +19,7 @@ #![feature(rustc_attrs)] #![cfg_attr(test, feature(test))] #![feature(strict_provenance)] +#![feature(ptr_const_cast)] use smallvec::SmallVec; @@ -27,7 +28,7 @@ use std::cell::{Cell, RefCell}; use std::cmp; use std::marker::{PhantomData, Send}; use std::mem::{self, MaybeUninit}; -use std::ptr; +use std::ptr::{self, NonNull}; use std::slice; #[inline(never)] @@ -55,15 +56,24 @@ pub struct TypedArena { struct ArenaChunk { /// The raw storage for the arena chunk. - storage: Box<[MaybeUninit]>, + storage: NonNull<[MaybeUninit]>, /// The number of valid entries in the chunk. entries: usize, } +unsafe impl<#[may_dangle] T> Drop for ArenaChunk { + fn drop(&mut self) { + unsafe { Box::from_raw(self.storage.as_mut()) }; + } +} + impl ArenaChunk { #[inline] unsafe fn new(capacity: usize) -> ArenaChunk { - ArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 } + ArenaChunk { + storage: NonNull::new(Box::into_raw(Box::new_uninit_slice(capacity))).unwrap(), + entries: 0, + } } /// Destroys this arena chunk. @@ -72,14 +82,15 @@ impl ArenaChunk { // The branch on needs_drop() is an -O1 performance optimization. // Without the branch, dropping TypedArena takes linear time. if mem::needs_drop::() { - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut self.storage[..len])); + let slice = &mut *(self.storage.as_mut()); + ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut slice[..len])); } } // Returns a pointer to the first allocated object. #[inline] fn start(&mut self) -> *mut T { - MaybeUninit::slice_as_mut_ptr(&mut self.storage) + self.storage.as_ptr() as *mut T } // Returns a pointer to the end of the allocated space. @@ -90,7 +101,7 @@ impl ArenaChunk { // A pointer as large as possible for zero-sized elements. ptr::invalid_mut(!0) } else { - self.start().add(self.storage.len()) + self.start().add((*self.storage.as_ptr()).len()) } } } @@ -274,7 +285,7 @@ impl TypedArena { // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2); + new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / elem_size / 2); new_cap *= 2; } else { new_cap = PAGE / elem_size; @@ -382,7 +393,7 @@ impl DroplessArena { // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2); + new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / 2); new_cap *= 2; } else { new_cap = PAGE; diff --git a/compiler/rustc_arena/src/tests.rs b/compiler/rustc_arena/src/tests.rs index 911e577c1edc7..ad61464343a4a 100644 --- a/compiler/rustc_arena/src/tests.rs +++ b/compiler/rustc_arena/src/tests.rs @@ -79,7 +79,11 @@ fn test_arena_alloc_nested() { #[test] pub fn test_copy() { let arena = TypedArena::default(); - for _ in 0..100000 { + #[cfg(not(miri))] + const N: usize = 100000; + #[cfg(miri)] + const N: usize = 1000; + for _ in 0..N { arena.alloc(Point { x: 1, y: 2, z: 3 }); } } @@ -106,7 +110,11 @@ struct Noncopy { #[test] pub fn test_noncopy() { let arena = TypedArena::default(); - for _ in 0..100000 { + #[cfg(not(miri))] + const N: usize = 100000; + #[cfg(miri)] + const N: usize = 1000; + for _ in 0..N { arena.alloc(Noncopy { string: "hello world".to_string(), array: vec![1, 2, 3, 4, 5] }); } } @@ -114,7 +122,11 @@ pub fn test_noncopy() { #[test] pub fn test_typed_arena_zero_sized() { let arena = TypedArena::default(); - for _ in 0..100000 { + #[cfg(not(miri))] + const N: usize = 100000; + #[cfg(miri)] + const N: usize = 1000; + for _ in 0..N { arena.alloc(()); } } @@ -124,7 +136,11 @@ pub fn test_typed_arena_clear() { let mut arena = TypedArena::default(); for _ in 0..10 { arena.clear(); - for _ in 0..10000 { + #[cfg(not(miri))] + const N: usize = 10000; + #[cfg(miri)] + const N: usize = 100; + for _ in 0..N { arena.alloc(Point { x: 1, y: 2, z: 3 }); } } diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 05457ce15e9a7..50d8fc30d7d7c 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -66,7 +66,11 @@ fn emit_module( let work_product = if backend_config.disable_incr_cache { None } else { - rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(tcx.sess, &name, &tmp_file) + rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( + tcx.sess, + &name, + &[("o", &tmp_file)], + ) }; ModuleCodegenResult( @@ -82,7 +86,10 @@ fn reuse_workproduct_for_cgu( ) -> CompiledModule { let work_product = cgu.previous_work_product(tcx); let obj_out = tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str())); - let source_file = rustc_incremental::in_incr_comp_dir_sess(&tcx.sess, &work_product.saved_file); + let source_file = rustc_incremental::in_incr_comp_dir_sess( + &tcx.sess, + &work_product.saved_files.get("o").expect("no saved object file in work product"), + ); if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) { tcx.sess.err(&format!( "unable to copy {} to {}: {}", diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 72aa790c36357..960e98243ac72 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -151,11 +151,23 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( return; } - let remove_temps_from_module = |module: &CompiledModule| { - if let Some(ref obj) = module.object { - ensure_removed(sess.diagnostic(), obj); - } - }; + let maybe_remove_temps_from_module = + |preserve_objects: bool, preserve_dwarf_objects: bool, module: &CompiledModule| { + if !preserve_objects { + if let Some(ref obj) = module.object { + ensure_removed(sess.diagnostic(), obj); + } + } + + if !preserve_dwarf_objects { + if let Some(ref dwo_obj) = module.dwarf_object { + ensure_removed(sess.diagnostic(), dwo_obj); + } + } + }; + + let remove_temps_from_module = + |module: &CompiledModule| maybe_remove_temps_from_module(false, false, module); // Otherwise, always remove the metadata and allocator module temporaries. if let Some(ref metadata_module) = codegen_results.metadata_module { @@ -177,15 +189,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( debug!(?preserve_objects, ?preserve_dwarf_objects); for module in &codegen_results.modules { - if !preserve_objects { - remove_temps_from_module(module); - } - - if !preserve_dwarf_objects { - if let Some(ref obj) = module.dwarf_object { - ensure_removed(sess.diagnostic(), obj); - } - } + maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module); } }); @@ -649,6 +653,7 @@ fn link_dwarf_object<'a>( sess.struct_err("linking dwarf objects with thorin failed") .note(&format!("{:?}", e)) .emit(); + sess.abort_if_errors(); } } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 632f07c5c2d80..f4a5cac872e05 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -494,12 +494,18 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir"); for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) { - if let Some(path) = &module.object { - if let Some((id, product)) = - copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, path) - { - work_products.insert(id, product); - } + let mut files = Vec::new(); + if let Some(object_file_path) = &module.object { + files.push(("o", object_file_path.as_path())); + } + if let Some(dwarf_object_file_path) = &module.dwarf_object { + files.push(("dwo", dwarf_object_file_path.as_path())); + } + + if let Some((id, product)) = + copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, files.as_slice()) + { + work_products.insert(id, product); } } @@ -856,29 +862,50 @@ fn execute_copy_from_cache_work_item( assert!(module_config.emit_obj != EmitObj::None); let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); - let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)); - let source_file = in_incr_comp_dir(&incr_comp_session_dir, &module.source.saved_file); - debug!( - "copying pre-existing module `{}` from {:?} to {}", - module.name, - source_file, - obj_out.display() + + let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { + let source_file = in_incr_comp_dir(&incr_comp_session_dir, saved_path); + debug!( + "copying pre-existing module `{}` from {:?} to {}", + module.name, + source_file, + output_path.display() + ); + match link_or_copy(&source_file, &output_path) { + Ok(_) => Some(output_path), + Err(err) => { + let diag_handler = cgcx.create_diag_handler(); + diag_handler.err(&format!( + "unable to copy {} to {}: {}", + source_file.display(), + output_path.display(), + err + )); + None + } + } + }; + + let object = load_from_incr_comp_dir( + cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)), + &module.source.saved_files.get("o").expect("no saved object file in work product"), ); - if let Err(err) = link_or_copy(&source_file, &obj_out) { - let diag_handler = cgcx.create_diag_handler(); - diag_handler.err(&format!( - "unable to copy {} to {}: {}", - source_file.display(), - obj_out.display(), - err - )); - } + let dwarf_object = + module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| { + let dwarf_obj_out = cgcx + .output_filenames + .split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, Some(&module.name)) + .expect( + "saved dwarf object in work product but `split_dwarf_path` returned `None`", + ); + load_from_incr_comp_dir(dwarf_obj_out, &saved_dwarf_object_file) + }); WorkItemResult::Compiled(CompiledModule { name: module.name, kind: ModuleKind::Regular, - object: Some(obj_out), - dwarf_object: None, + object, + dwarf_object, bytecode: None, }) } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 7b9c6329d326e..56ecc5535285b 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -12,6 +12,7 @@ use rustc_middle::mir::{ Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK, }; use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::always_live_locals; @@ -275,7 +276,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } }; - match parent_ty.ty.kind() { + let kind = match parent_ty.ty.kind() { + &ty::Opaque(def_id, substs) => { + self.tcx.bound_type_of(def_id).subst(self.tcx, substs).kind() + } + kind => kind, + }; + + match kind { ty::Tuple(fields) => { let Some(f_ty) = fields.get(f.as_usize()) else { fail_out_of_bounds(self, location); @@ -299,12 +307,39 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; check_equal(self, location, f_ty); } - ty::Generator(_, substs, _) => { - let substs = substs.as_generator(); - let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else { - fail_out_of_bounds(self, location); - return; + &ty::Generator(def_id, substs, _) => { + let f_ty = if let Some(var) = parent_ty.variant_index { + let gen_body = if def_id == self.body.source.def_id() { + self.body + } else { + self.tcx.optimized_mir(def_id) + }; + + let Some(layout) = gen_body.generator_layout() else { + self.fail(location, format!("No generator layout for {:?}", parent_ty)); + return; + }; + + let Some(&local) = layout.variant_fields[var].get(f) else { + fail_out_of_bounds(self, location); + return; + }; + + let Some(&f_ty) = layout.field_tys.get(local) else { + self.fail(location, format!("Out of bounds local {:?} for {:?}", local, parent_ty)); + return; + }; + + f_ty + } else { + let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else { + fail_out_of_bounds(self, location); + return; + }; + + f_ty }; + check_equal(self, location, f_ty); } _ => { @@ -328,6 +363,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { { self.fail(location, format!("{:?}, has deref at the wrong place", place)); } + + self.super_place(place, cntxt, location); } fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 9c325faae8058..f59d8d596b98e 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -161,19 +161,13 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { Decodable::decode(&mut work_product_decoder); for swp in work_products { - let mut all_files_exist = true; - let path = in_incr_comp_dir_sess(sess, &swp.work_product.saved_file); - if !path.exists() { - all_files_exist = false; - - if sess.opts.debugging_opts.incremental_info { - eprintln!( - "incremental: could not find file for work \ - product: {}", - path.display() - ); + let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| { + let exists = in_incr_comp_dir_sess(sess, path).exists(); + if !exists && sess.opts.debugging_opts.incremental_info { + eprintln!("incremental: could not find file for work product: {path}",); } - } + exists + }); if all_files_exist { debug!("reconcile_work_products: all files for {:?} exist", swp); diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 79836d66011a2..4059b7cfc8eb9 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -108,16 +108,17 @@ pub fn save_work_product_index( for (id, wp) in previous_work_products.iter() { if !new_work_products.contains_key(id) { work_product::delete_workproduct_files(sess, wp); - debug_assert!(!in_incr_comp_dir_sess(sess, &wp.saved_file).exists()); + debug_assert!( + !wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + ); } } // Check that we did not delete one of the current work-products: debug_assert!({ - new_work_products - .iter() - .map(|(_, wp)| in_incr_comp_dir_sess(sess, &wp.saved_file)) - .all(|path| path.exists()) + new_work_products.iter().all(|(_, wp)| { + wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + }) }); } diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 4789c0f581fdb..1b184eca964c3 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -3,6 +3,7 @@ //! [work products]: WorkProduct use crate::persist::fs::*; +use rustc_data_structures::stable_map::FxHashMap; use rustc_fs_util::link_or_copy; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -13,38 +14,41 @@ use std::path::Path; pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( sess: &Session, cgu_name: &str, - path: &Path, + files: &[(&'static str, &Path)], ) -> Option<(WorkProductId, WorkProduct)> { - debug!("copy_cgu_workproduct_to_incr_comp_cache_dir({:?},{:?})", cgu_name, path); + debug!(?cgu_name, ?files); sess.opts.incremental.as_ref()?; - let file_name = format!("{}.o", cgu_name); - let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); - let saved_file = match link_or_copy(path, &path_in_incr_dir) { - Ok(_) => file_name, - Err(err) => { - sess.warn(&format!( - "error copying object file `{}` to incremental directory as `{}`: {}", - path.display(), - path_in_incr_dir.display(), - err - )); - return None; + let mut saved_files = FxHashMap::default(); + for (ext, path) in files { + let file_name = format!("{cgu_name}.{ext}"); + let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); + match link_or_copy(path, &path_in_incr_dir) { + Ok(_) => { + let _ = saved_files.insert(ext.to_string(), file_name); + } + Err(err) => { + sess.warn(&format!( + "error copying object file `{}` to incremental directory as `{}`: {}", + path.display(), + path_in_incr_dir.display(), + err + )); + } } - }; - - let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_file }; + } + let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_files }; + debug!(?work_product); let work_product_id = WorkProductId::from_cgu_name(cgu_name); Some((work_product_id, work_product)) } /// Removes files for a given work product. pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { - let path = in_incr_comp_dir_sess(sess, &work_product.saved_file); - match std_fs::remove_file(&path) { - Ok(()) => {} - Err(err) => { + for (_, path) in &work_product.saved_files { + let path = in_incr_comp_dir_sess(sess, path); + if let Err(err) = std_fs::remove_file(&path) { sess.warn(&format!( "file-system error deleting outdated file `{}`: {}", path.display(), diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 83328093e9fa8..5725c240320ad 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -34,7 +34,7 @@ use rustc_middle::middle::stability; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt}; -use rustc_session::lint::BuiltinLintDiagnostics; +use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId}; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; use rustc_session::Session; use rustc_span::lev_distance::find_best_match_for_name; @@ -906,6 +906,29 @@ pub trait LintContext: Sized { ) { self.lookup(lint, None as Option, decorate); } + + /// This returns the lint level for the given lint at the current location. + fn get_lint_level(&self, lint: &'static Lint) -> Level; + + /// This function can be used to manually fulfill an expectation. This can + /// be used for lints which contain several spans, and should be suppressed, + /// if either location was marked with an expectation. + /// + /// Note that this function should only be called for [`LintExpectationId`]s + /// retrieved from the current lint pass. Buffered or manually created ids can + /// cause ICEs. + fn fulfill_expectation(&self, expectation: LintExpectationId) { + // We need to make sure that submitted expectation ids are correctly fulfilled suppressed + // and stored between compilation sessions. To not manually do these steps, we simply create + // a dummy diagnostic and emit is as usual, which will be suppressed and stored like a normal + // expected lint diagnostic. + self.sess() + .struct_expect( + "this is a dummy diagnostic, to submit and store an expectation", + expectation, + ) + .emit(); + } } impl<'a> EarlyContext<'a> { @@ -953,6 +976,10 @@ impl LintContext for LateContext<'_> { None => self.tcx.struct_lint_node(lint, hir_id, decorate), } } + + fn get_lint_level(&self, lint: &'static Lint) -> Level { + self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0 + } } impl LintContext for EarlyContext<'_> { @@ -975,6 +1002,10 @@ impl LintContext for EarlyContext<'_> { ) { self.builder.struct_lint(lint, span.map(|s| s.into()), decorate) } + + fn get_lint_level(&self, lint: &'static Lint) -> Level { + self.builder.lint_level(lint).0 + } } impl<'tcx> LateContext<'tcx> { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 40601bb5aad47..9fc2249b29019 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -520,6 +520,11 @@ declare_lint! { /// The `expect` attribute can be removed if this is intended behavior otherwise /// it should be investigated why the expected lint is no longer issued. /// + /// In rare cases, the expectation might be emitted at a different location than + /// shown in the shown code snippet. In most cases, the `#[expect]` attribute + /// works when added to the outer scope. A few lints can only be expected + /// on a crate level. + /// /// Part of RFC 2383. The progress is being tracked in [#54503] /// /// [#54503]: https://github.com/rust-lang/rust/issues/54503 diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1cd19c7eaab35..48f441e69d642 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -232,6 +232,13 @@ impl Level { Level::Deny | Level::Forbid => true, } } + + pub fn get_expectation_id(&self) -> Option { + match self { + Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id), + _ => None, + } + } } /// Specification of a single lint. diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 341cf8f827bc9..3291717c550df 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -886,8 +886,12 @@ impl DepGraph { #[derive(Clone, Debug, Encodable, Decodable)] pub struct WorkProduct { pub cgu_name: String, - /// Saved file associated with this CGU. - pub saved_file: String, + /// Saved files associated with this CGU. In each key/value pair, the value is the path to the + /// saved file and the key is some identifier for the type of file being saved. + /// + /// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to + /// the object file's path, and "dwo" to the dwarf object file's path. + pub saved_files: FxHashMap, } // Index type for `DepNodeData`'s edges. diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 9ed4a98f943cd..273477968e036 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -41,7 +41,7 @@ mod ip; mod parser; mod tcp; #[cfg(test)] -mod test; +pub(crate) mod test; mod udp; /// Possible values which can be passed to the [`TcpStream::shutdown`] method. diff --git a/library/std/src/os/android/mod.rs b/library/std/src/os/android/mod.rs index dbb0127f369fd..5adcb82b6a49b 100644 --- a/library/std/src/os/android/mod.rs +++ b/library/std/src/os/android/mod.rs @@ -3,4 +3,5 @@ #![stable(feature = "raw_ext", since = "1.1.0")] pub mod fs; +pub mod net; pub mod raw; diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs new file mode 100644 index 0000000000000..ff96125c37bdc --- /dev/null +++ b/library/std/src/os/android/net.rs @@ -0,0 +1,4 @@ +//! Linux and Android-specific definitions for socket options. + +#![unstable(feature = "tcp_quickack", issue = "96256")] +pub use crate::os::net::tcp::TcpStreamExt; diff --git a/library/std/src/os/linux/mod.rs b/library/std/src/os/linux/mod.rs index 8e7776f6646b5..c17053011adfc 100644 --- a/library/std/src/os/linux/mod.rs +++ b/library/std/src/os/linux/mod.rs @@ -4,5 +4,6 @@ #![doc(cfg(target_os = "linux"))] pub mod fs; +pub mod net; pub mod process; pub mod raw; diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs new file mode 100644 index 0000000000000..ff96125c37bdc --- /dev/null +++ b/library/std/src/os/linux/net.rs @@ -0,0 +1,4 @@ +//! Linux and Android-specific definitions for socket options. + +#![unstable(feature = "tcp_quickack", issue = "96256")] +pub use crate::os::net::tcp::TcpStreamExt; diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index a1df72a8a0480..ff9ea118d971e 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -149,3 +149,6 @@ pub mod vxworks; #[cfg(any(unix, target_os = "wasi", doc))] mod fd; + +#[cfg(any(target_os = "linux", target_os = "android",))] +mod net; diff --git a/library/std/src/os/net/mod.rs b/library/std/src/os/net/mod.rs new file mode 100644 index 0000000000000..d6d84d24ec489 --- /dev/null +++ b/library/std/src/os/net/mod.rs @@ -0,0 +1,7 @@ +//! Linux and Android-specific definitions for socket options. + +#![unstable(feature = "tcp_quickack", issue = "96256")] +#![doc(cfg(any(target_os = "linux", target_os = "android",)))] +pub mod tcp; +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/net/tcp.rs b/library/std/src/os/net/tcp.rs new file mode 100644 index 0000000000000..5e9ee65a4152e --- /dev/null +++ b/library/std/src/os/net/tcp.rs @@ -0,0 +1,70 @@ +//! Linux and Android-specific tcp extensions to primitives in the [`std::net`] module. +//! +//! [`std::net`]: crate::net + +use crate::io; +use crate::net; +use crate::sealed::Sealed; +use crate::sys_common::AsInner; + +/// Os-specific extensions for [`TcpStream`] +/// +/// [`TcpStream`]: net::TcpStream +#[unstable(feature = "tcp_quickack", issue = "96256")] +pub trait TcpStreamExt: Sealed { + /// Enable or disable `TCP_QUICKACK`. + /// + /// This flag causes Linux to eagerly send ACKs rather than delaying them. + /// Linux may reset this flag after further operations on the socket. + /// + /// See [`man 7 tcp`](https://man7.org/linux/man-pages/man7/tcp.7.html) and + /// [TCP delayed acknowledgement](https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment) + /// for more information. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(tcp_quickack)] + /// use std::net::TcpStream; + /// use std::os::linux::net::TcpStreamExt; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_quickack(true).expect("set_quickack call failed"); + /// ``` + #[unstable(feature = "tcp_quickack", issue = "96256")] + fn set_quickack(&self, quickack: bool) -> io::Result<()>; + + /// Gets the value of the `TCP_QUICKACK` option on this socket. + /// + /// For more information about this option, see [`TcpStreamExt::set_quickack`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(tcp_quickack)] + /// use std::net::TcpStream; + /// use std::os::linux::net::TcpStreamExt; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_quickack(true).expect("set_quickack call failed"); + /// assert_eq!(stream.quickack().unwrap_or(false), true); + /// ``` + #[unstable(feature = "tcp_quickack", issue = "96256")] + fn quickack(&self) -> io::Result; +} + +#[unstable(feature = "tcp_quickack", issue = "96256")] +impl Sealed for net::TcpStream {} + +#[unstable(feature = "tcp_quickack", issue = "96256")] +impl TcpStreamExt for net::TcpStream { + fn set_quickack(&self, quickack: bool) -> io::Result<()> { + self.as_inner().as_inner().set_quickack(quickack) + } + + fn quickack(&self) -> io::Result { + self.as_inner().as_inner().quickack() + } +} diff --git a/library/std/src/os/net/tests.rs b/library/std/src/os/net/tests.rs new file mode 100644 index 0000000000000..4704e3156913c --- /dev/null +++ b/library/std/src/os/net/tests.rs @@ -0,0 +1,29 @@ +#[cfg(any(target_os = "android", target_os = "linux",))] +#[test] +fn quickack() { + use crate::{ + net::{test::next_test_ip4, TcpListener, TcpStream}, + os::net::tcp::TcpStreamExt, + }; + + macro_rules! t { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), + } + }; + } + + let addr = next_test_ip4(); + let _listener = t!(TcpListener::bind(&addr)); + + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + + t!(stream.set_quickack(false)); + assert_eq!(false, t!(stream.quickack())); + t!(stream.set_quickack(true)); + assert_eq!(true, t!(stream.quickack())); + t!(stream.set_quickack(false)); + assert_eq!(false, t!(stream.quickack())); +} diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 8008acfd1c96f..02d0f24cd6526 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -838,6 +838,24 @@ impl UnixDatagram { self.0.passcred() } + /// Set the id of the socket for network filtering purpose + /// + /// ```no_run + /// #![feature(unix_set_mark)] + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_mark(32)?; + /// Ok(()) + /// } + /// ``` + #[cfg(any(doc, target_os = "linux", target_os = "freebsd", target_os = "openbsd",))] + #[unstable(feature = "unix_set_mark", issue = "96467")] + pub fn set_mark(&self, mark: u32) -> io::Result<()> { + self.0.set_mark(mark) + } + /// Returns the value of the `SO_ERROR` option. /// /// # Examples diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 1d6083e66e172..ece0f91dad029 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -424,6 +424,24 @@ impl UnixStream { self.0.passcred() } + /// Set the id of the socket for network filtering purpose + /// + /// ```no_run + /// #![feature(unix_set_mark)] + /// use std::os::unix::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixStream::connect("/tmp/sock")?; + /// sock.set_mark(32)?; + /// Ok(()) + /// } + /// ``` + #[cfg(any(doc, target_os = "linux", target_os = "freebsd", target_os = "openbsd",))] + #[unstable(feature = "unix_set_mark", issue = "96467")] + pub fn set_mark(&self, mark: u32) -> io::Result<()> { + self.0.set_mark(mark) + } + /// Returns the value of the `SO_ERROR` option. /// /// # Examples diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index a1bbc2d87b640..bebe59564580a 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -392,6 +392,17 @@ impl Socket { Ok(raw != 0) } + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn quickack(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; + Ok(raw != 0) + } + #[cfg(any(target_os = "android", target_os = "linux",))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) @@ -427,6 +438,17 @@ impl Socket { self.0.set_nonblocking(nonblocking) } + #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] + pub fn set_mark(&self, mark: u32) -> io::Result<()> { + #[cfg(target_os = "linux")] + let option = libc::SO_MARK; + #[cfg(target_os = "freebsd")] + let option = libc::SO_USER_COOKIE; + #[cfg(target_os = "openbsd")] + let option = libc::SO_RTABLE; + setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) + } + pub fn take_error(&self) -> io::Result> { let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index f5730a2cea52b..579946cf8ca4c 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -345,6 +345,12 @@ impl TcpStream { } } +impl AsInner for TcpStream { + fn as_inner(&self) -> &Socket { + &self.inner + } +} + impl FromInner for TcpStream { fn from_inner(socket: Socket) -> TcpStream { TcpStream { inner: socket } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 4d33b7a376a23..efa73a79e9973 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1705,8 +1705,8 @@ fn clean_ty<'tcx>(this: Ty<'tcx>, cx: &mut DocContext<'tcx>, def_id: Option Tuple(vec![]), // FIXME(pcwalton) - + ty::Closure(..) => panic!("Closure"), + ty::Generator(..) => panic!("Generator"), ty::Bound(..) => panic!("Bound"), ty::Placeholder(..) => panic!("Placeholder"), ty::GeneratorWitness(..) => panic!("GeneratorWitness"), @@ -1760,7 +1760,6 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool { match tcx.def_kind(parent) { DefKind::Struct | DefKind::Union => false, DefKind::Variant => true, - // FIXME: what about DefKind::Ctor? parent_kind => panic!("unexpected parent kind: {:?}", parent_kind), } } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 04638aa1af62b..7ec6eb0be6527 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -237,9 +237,6 @@ pub(crate) struct RenderOptions { pub(crate) resource_suffix: String, /// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by /// default. - // - // FIXME(misdreavus): the flag name is `--disable-minification` but the meaning is inverted - // once read. pub(crate) enable_minification: bool, /// Whether to create an index page in the root of the output directory. If this is true but /// `enable_index_page` is None, generate a static listing of crates instead. diff --git a/src/test/incremental/split_debuginfo_cached.rs b/src/test/incremental/split_debuginfo_cached.rs new file mode 100644 index 0000000000000..25c802d5a1d2e --- /dev/null +++ b/src/test/incremental/split_debuginfo_cached.rs @@ -0,0 +1,25 @@ +// Check that compiling with packed Split DWARF twice succeeds. This should confirm that DWARF +// objects are cached as work products and available to the incremental compilation for `thorin` to +// pack into a DWARF package. + +// ignore-tidy-linelength +// only-x86_64-unknown-linux-gnu +// revisions:rpass1 rpass2 + +// [rpass1]compile-flags: -g -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split +// [rpass2]compile-flags: -g -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split + +#![feature(rustc_attrs)] +// For `rpass2`, nothing has changed so everything should re-used. +#![rustc_partition_reused(module = "split_debuginfo_cached", cfg = "rpass2")] +#![rustc_partition_reused(module = "split_debuginfo_cached-another_module", cfg = "rpass2")] + +mod another_module { + pub fn foo() -> &'static str { + "hello world" + } +} + +pub fn main() { + println!("{}", another_module::foo()); +} diff --git a/src/test/ui/mir/ssa-analysis-regression-50041.rs b/src/test/ui/mir/ssa-analysis-regression-50041.rs index 8e9c14a03c386..ebc3e2f8c0e31 100644 --- a/src/test/ui/mir/ssa-analysis-regression-50041.rs +++ b/src/test/ui/mir/ssa-analysis-regression-50041.rs @@ -5,7 +5,7 @@ #![feature(lang_items)] #![no_std] -struct NonNull(*mut T); +struct NonNull(*const T); struct Unique(NonNull); @@ -23,7 +23,7 @@ unsafe fn box_free(ptr: Unique) { } #[inline(never)] -fn dealloc(_: *mut T) {} +fn dealloc(_: *const T) {} pub struct Foo(T); diff --git a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs index c6c7b959d4f49..4f49bb879f503 100644 --- a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs +++ b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind}; use rustc_errors::MultiSpan; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext, Level}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{FileName, Span}; use std::collections::BTreeMap; @@ -49,6 +49,7 @@ declare_clippy_lint! { struct Modules { local_path: PathBuf, spans: Vec, + lint_levels: Vec, } #[derive(Default)] @@ -70,13 +71,30 @@ impl EarlyLintPass for DuplicateMod { let modules = self.modules.entry(absolute_path).or_insert(Modules { local_path, spans: Vec::new(), + lint_levels: Vec::new(), }); modules.spans.push(item.span_with_attributes()); + modules.lint_levels.push(cx.get_lint_level(DUPLICATE_MOD)); } } fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) { - for Modules { local_path, spans } in self.modules.values() { + for Modules { local_path, spans, lint_levels } in self.modules.values() { + if spans.len() < 2 { + continue; + } + + // At this point the lint would be emitted + assert_eq!(spans.len(), lint_levels.len()); + let spans: Vec<_> = spans.into_iter().zip(lint_levels).filter_map(|(span, lvl)|{ + if let Some(id) = lvl.get_expectation_id() { + cx.fulfill_expectation(id); + } + + (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span) + }) + .collect(); + if spans.len() < 2 { continue; } diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs index 79b343da24700..99ca538b6e4a5 100644 --- a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs +++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs @@ -1,3 +1,5 @@ +#[feature(lint_reasons)] + mod a; mod b; @@ -13,4 +15,15 @@ mod c3; mod from_other_module; mod other_module; +mod d; +#[path = "d.rs"] +mod d2; +#[path = "d.rs"] +#[expect(clippy::duplicate_mod)] +mod d3; +#[path = "d.rs"] +#[allow(clippy::duplicate_mod)] +mod d4; + + fn main() {} diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr index 00d7739c8a2ea..61df1ad5d501a 100644 --- a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr +++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr @@ -1,5 +1,5 @@ error: file is loaded as a module multiple times: `$DIR/b.rs` - --> $DIR/main.rs:3:1 + --> $DIR/main.rs:5:1 | LL | mod b; | ^^^^^^ first loaded here @@ -11,7 +11,7 @@ LL | | mod b2; = help: replace all but one `mod` item with `use` items error: file is loaded as a module multiple times: `$DIR/c.rs` - --> $DIR/main.rs:7:1 + --> $DIR/main.rs:9:1 | LL | mod c; | ^^^^^^ first loaded here @@ -25,7 +25,7 @@ LL | | mod c3; = help: replace all but one `mod` item with `use` items error: file is loaded as a module multiple times: `$DIR/from_other_module.rs` - --> $DIR/main.rs:13:1 + --> $DIR/main.rs:15:1 | LL | mod from_other_module; | ^^^^^^^^^^^^^^^^^^^^^^ first loaded here @@ -38,5 +38,16 @@ LL | | mod m; | = help: replace all but one `mod` item with `use` items -error: aborting due to 3 previous errors +error: file is loaded as a module multiple times: `$DIR/b.rs` + --> $DIR/main.rs:18:1 + | +LL | mod d; + | ^^^^^^ first loaded here +LL | / #[path = "d.rs"] +LL | | mod d2; + | |_______^ loaded again here + | + = help: replace all but one `mod` item with `use` items + +error: aborting due to 4 previous errors