diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d1056de25c10..cd13ce35007b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -315,6 +315,10 @@ jobs: - name: x86_64-gnu-distcheck os: ubuntu-20.04-8core-32gb env: {} + - name: x86_64-gnu-llvm-18 + env: + RUST_BACKTRACE: 1 + os: ubuntu-20.04-8core-32gb - name: x86_64-gnu-llvm-17 env: RUST_BACKTRACE: 1 diff --git a/Cargo.lock b/Cargo.lock index 218fa5a6842e0..1254421aa5842 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4133,7 +4133,6 @@ dependencies = [ "rustc_target", "rustc_trait_selection", "rustc_type_ir", - "smallvec", "tracing", "unicode-security", ] diff --git a/compiler/rustc_ast_ir/src/visit.rs b/compiler/rustc_ast_ir/src/visit.rs index dec9f7a47d09d..f6d6bf3a3e309 100644 --- a/compiler/rustc_ast_ir/src/visit.rs +++ b/compiler/rustc_ast_ir/src/visit.rs @@ -14,7 +14,7 @@ impl VisitorResult for () { type Residual = !; #[cfg(not(feature = "nightly"))] - type Residual = core::ops::Infallible; + type Residual = core::convert::Infallible; fn output() -> Self {} fn from_residual(_: Self::Residual) -> Self {} diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 2da9bda19e034..134a6baea6e8b 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -3,18 +3,20 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; +use rustc_data_structures::sync::Lrc; use rustc_expand::base::{ - check_zero_tts, get_single_str_from_tts, parse_expr, resolve_path, DummyResult, ExtCtxt, - MacEager, MacResult, + check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr, + resolve_path, DummyResult, ExtCtxt, MacEager, MacResult, }; use rustc_expand::module::DirOwnership; use rustc_parse::new_parser_from_file; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; +use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; use rustc_span::{Pos, Span}; - use smallvec::SmallVec; +use std::path::{Path, PathBuf}; use std::rc::Rc; // These macros all relate to the file system; they either return @@ -180,32 +182,22 @@ pub fn expand_include_str( tts: TokenStream, ) -> Box { let sp = cx.with_def_site_ctxt(sp); - let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") { - Ok(file) => file, + let (path, path_span) = match get_single_str_spanned_from_tts(cx, sp, tts, "include_str!") { + Ok(res) => res, Err(guar) => return DummyResult::any(sp, guar), }; - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, - Err(err) => { - let guar = err.emit(); - return DummyResult::any(sp, guar); - } - }; - match cx.source_map().load_binary_file(&file) { + match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { Ok(bytes) => match std::str::from_utf8(&bytes) { Ok(src) => { let interned_src = Symbol::intern(src); MacEager::expr(cx.expr_str(sp, interned_src)) } Err(_) => { - let guar = cx.dcx().span_err(sp, format!("{} wasn't a utf-8 file", file.display())); + let guar = cx.dcx().span_err(sp, format!("`{path}` wasn't a utf-8 file")); DummyResult::any(sp, guar) } }, - Err(e) => { - let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - DummyResult::any(sp, guar) - } + Err(dummy) => dummy, } } @@ -215,25 +207,123 @@ pub fn expand_include_bytes( tts: TokenStream, ) -> Box { let sp = cx.with_def_site_ctxt(sp); - let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") { - Ok(file) => file, + let (path, path_span) = match get_single_str_spanned_from_tts(cx, sp, tts, "include_bytes!") { + Ok(res) => res, Err(guar) => return DummyResult::any(sp, guar), }; - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, + match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { + Ok(bytes) => { + let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes)); + MacEager::expr(expr) + } + Err(dummy) => dummy, + } +} + +fn load_binary_file( + cx: &mut ExtCtxt<'_>, + original_path: &Path, + macro_span: Span, + path_span: Span, +) -> Result, Box> { + let resolved_path = match resolve_path(&cx.sess, original_path, macro_span) { + Ok(path) => path, Err(err) => { let guar = err.emit(); - return DummyResult::any(sp, guar); + return Err(DummyResult::any(macro_span, guar)); } }; - match cx.source_map().load_binary_file(&file) { - Ok(bytes) => { - let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes)); - MacEager::expr(expr) + match cx.source_map().load_binary_file(&resolved_path) { + Ok(data) => Ok(data), + Err(io_err) => { + let mut err = cx.dcx().struct_span_err( + macro_span, + format!("couldn't read `{}`: {io_err}", resolved_path.display()), + ); + + if original_path.is_relative() { + let source_map = cx.sess.source_map(); + let new_path = source_map + .span_to_filename(macro_span.source_callsite()) + .into_local_path() + .and_then(|src| find_path_suggestion(source_map, src.parent()?, original_path)) + .and_then(|path| path.into_os_string().into_string().ok()); + + if let Some(new_path) = new_path { + err.span_suggestion( + path_span, + "there is a file with the same name in a different directory", + format!("\"{}\"", new_path.escape_debug()), + rustc_lint_defs::Applicability::MachineApplicable, + ); + } + } + let guar = err.emit(); + Err(DummyResult::any(macro_span, guar)) } - Err(e) => { - let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - DummyResult::any(sp, guar) + } +} + +fn find_path_suggestion( + source_map: &SourceMap, + base_dir: &Path, + wanted_path: &Path, +) -> Option { + // Fix paths that assume they're relative to cargo manifest dir + let mut base_c = base_dir.components(); + let mut wanted_c = wanted_path.components(); + let mut without_base = None; + while let Some(wanted_next) = wanted_c.next() { + if wanted_c.as_path().file_name().is_none() { + break; + } + // base_dir may be absolute + while let Some(base_next) = base_c.next() { + if base_next == wanted_next { + without_base = Some(wanted_c.as_path()); + break; + } + } + } + let root_absolute = without_base.into_iter().map(PathBuf::from); + + let base_dir_components = base_dir.components().count(); + // Avoid going all the way to the root dir + let max_parent_components = if base_dir.is_relative() { + base_dir_components + 1 + } else { + base_dir_components.saturating_sub(1) + }; + + // Try with additional leading ../ + let mut prefix = PathBuf::new(); + let add = std::iter::from_fn(|| { + prefix.push(".."); + Some(prefix.join(wanted_path)) + }) + .take(max_parent_components.min(3)); + + // Try without leading directories + let mut trimmed_path = wanted_path; + let remove = std::iter::from_fn(|| { + let mut components = trimmed_path.components(); + let removed = components.next()?; + trimmed_path = components.as_path(); + let _ = trimmed_path.file_name()?; // ensure there is a file name left + Some([ + Some(trimmed_path.to_path_buf()), + (removed != std::path::Component::ParentDir) + .then(|| Path::new("..").join(trimmed_path)), + ]) + }) + .flatten() + .flatten() + .take(4); + + for new_path in root_absolute.chain(add).chain(remove) { + if source_map.file_exists(&base_dir.join(&new_path)) { + return Some(new_path); } } + None } diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 81c9e02e2ee80..f3af633b4e561 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -146,9 +146,6 @@ const_eval_intern_kind = {$kind -> *[other] {""} } -const_eval_invalid_align = - align has to be a power of 2 - const_eval_invalid_align_details = invalid align passed to `{$name}`: {$align} is {$err_kind -> [not_power_of_two] not a power of 2 diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 69dfb48919cdb..58589ebdca675 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1233,21 +1233,18 @@ pub fn resolve_path(sess: &Session, path: impl Into, span: Span) -> PRe // after macro expansion (that is, they are unhygienic). if !path.is_absolute() { let callsite = span.source_callsite(); - let mut result = match sess.source_map().span_to_filename(callsite) { - FileName::Real(name) => name - .into_local_path() - .expect("attempting to resolve a file path in an external file"), - FileName::DocTest(path, _) => path, - other => { - return Err(sess.dcx().create_err(errors::ResolveRelativePath { - span, - path: sess.source_map().filename_for_diagnostics(&other).to_string(), - })); - } + let source_map = sess.source_map(); + let Some(mut base_path) = source_map.span_to_filename(callsite).into_local_path() else { + return Err(sess.dcx().create_err(errors::ResolveRelativePath { + span, + path: source_map + .filename_for_diagnostics(&source_map.span_to_filename(callsite)) + .to_string(), + })); }; - result.pop(); - result.push(path); - Ok(result) + base_path.pop(); + base_path.push(path); + Ok(base_path) } else { Ok(path) } @@ -1344,6 +1341,15 @@ pub fn get_single_str_from_tts( tts: TokenStream, name: &str, ) -> Result { + get_single_str_spanned_from_tts(cx, span, tts, name).map(|(s, _)| s) +} + +pub fn get_single_str_spanned_from_tts( + cx: &mut ExtCtxt<'_>, + span: Span, + tts: TokenStream, + name: &str, +) -> Result<(Symbol, Span), ErrorGuaranteed> { let mut p = cx.new_parser_from_tts(tts); if p.token == token::Eof { let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); @@ -1355,7 +1361,12 @@ pub fn get_single_str_from_tts( if p.token != token::Eof { cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); } - expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s) + expr_to_spanned_string(cx, ret, "argument must be a string literal") + .map_err(|err| match err { + Ok((err, _)) => err.emit(), + Err(guar) => guar, + }) + .map(|(symbol, _style, span)| (symbol, span)) } /// Extracts comma-separated expressions from `tts`. diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 2de87cbe631ac..e44a6ae3b3f2e 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -181,14 +181,6 @@ infer_more_targeted = {$has_param_name -> infer_msl_introduces_static = introduces a `'static` lifetime requirement infer_msl_unmet_req = because this has an unmet lifetime requirement -infer_need_type_info_in_coroutine = - type inside {$coroutine_kind -> - [async_block] `async` block - [async_closure] `async` closure - [async_fn] `async fn` body - *[coroutine] coroutine - } must be known in this context - infer_nothing = {""} diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0d50200133cb2..23bd2dac57e3b 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -101,10 +101,11 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( threads: usize, f: F, ) -> R { - use rustc_data_structures::{jobserver, sync::FromDyn}; + use rustc_data_structures::{defer, jobserver, sync::FromDyn}; use rustc_middle::ty::tls; use rustc_query_impl::QueryCtxt; - use rustc_query_system::query::{deadlock, QueryContext}; + use rustc_query_system::query::{break_query_cycles, QueryContext}; + use std::process; let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); @@ -128,7 +129,19 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( let query_map = FromDyn::from(tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs())); let registry = rayon_core::Registry::current(); - thread::spawn(move || deadlock(query_map.into_inner(), ®istry)); + thread::Builder::new() + .name("rustc query cycle handler".to_string()) + .spawn(move || { + let on_panic = defer(|| { + eprintln!("query cycle handler thread panicked, aborting process"); + // We need to abort here as we failed to resolve the deadlock, + // otherwise the compiler could just hang, + process::abort(); + }); + break_query_cycles(query_map.into_inner(), ®istry); + on_panic.disable(); + }) + .unwrap(); }); if let Some(size) = get_stack_size() { builder = builder.stack_size(size); diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 2271321b8bf22..fa1133e7780ff 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -23,7 +23,6 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_type_ir = { path = "../rustc_type_ir" } -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" unicode-security = "0.1.0" # tidy-alphabetical-end diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 63e2fe47659db..8bf9d0b9d4aac 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -562,8 +562,6 @@ lint_suspicious_double_ref_clone = lint_suspicious_double_ref_deref = using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type -lint_trivial_untranslatable_diag = diagnostic with static strings only - lint_ty_qualified = usage of qualified `ty::{$ty}` .suggestion = try importing it and using it unqualified diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index a4fd5a7c45f97..7c4d92d3ce038 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -2,8 +2,6 @@ use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, Path, QPath, TyKind}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind}; -use smallvec::{smallvec, SmallVec}; - use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext}; @@ -85,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { if let Some(def_id) = oexpn.macro_def_id && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind && def_id.krate != LOCAL_CRATE - && std::env::var_os("CARGO").is_some() + && rustc_session::utils::was_invoked_from_cargo() { Some(NonLocalDefinitionsCargoUpdateNote { macro_kind: macro_kind.descr(), @@ -114,25 +112,25 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // is using local items and so we don't lint on it. // We also ignore anon-const in item by including the anon-const - // parent as well; and since it's quite uncommon, we use smallvec - // to avoid unnecessary heap allocations. - let local_parents: SmallVec<[DefId; 1]> = if parent_def_kind == DefKind::Const + // parent as well. + let parent_parent = if parent_def_kind == DefKind::Const && parent_opt_item_name == Some(kw::Underscore) { - smallvec![parent, cx.tcx.parent(parent)] + Some(cx.tcx.parent(parent)) } else { - smallvec![parent] + None }; let self_ty_has_local_parent = match impl_.self_ty.kind { TyKind::Path(QPath::Resolved(_, ty_path)) => { - path_has_local_parent(ty_path, cx, &*local_parents) + path_has_local_parent(ty_path, cx, parent, parent_parent) } TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => { path_has_local_parent( principle_poly_trait_ref.trait_ref.path, cx, - &*local_parents, + parent, + parent_parent, ) } TyKind::TraitObject([], _, _) @@ -154,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { let of_trait_has_local_parent = impl_ .of_trait - .map(|of_trait| path_has_local_parent(of_trait.path, cx, &*local_parents)) + .map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent)) .unwrap_or(false); // If none of them have a local parent (LOGICAL NOR) this means that @@ -218,6 +216,16 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { /// std::convert::PartialEq> /// ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` -fn path_has_local_parent(path: &Path<'_>, cx: &LateContext<'_>, local_parents: &[DefId]) -> bool { - path.res.opt_def_id().is_some_and(|did| local_parents.contains(&cx.tcx.parent(did))) +fn path_has_local_parent( + path: &Path<'_>, + cx: &LateContext<'_>, + impl_parent: DefId, + impl_parent_parent: Option, +) -> bool { + path.res.opt_def_id().is_some_and(|did| { + did.is_local() && { + let res_parent = cx.tcx.parent(did); + res_parent == impl_parent || Some(res_parent) == impl_parent_parent + } + }) } diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index dcccace12b00a..f957b02b2828c 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -243,7 +243,6 @@ use std::{cmp, fmt}; pub(crate) struct CrateLocator<'a> { // Immutable per-session configuration. only_needs_metadata: bool, - sysroot: &'a Path, metadata_loader: &'a dyn MetadataLoader, cfg_version: &'static str, @@ -319,7 +318,6 @@ impl<'a> CrateLocator<'a> { CrateLocator { only_needs_metadata, - sysroot: &sess.sysroot, metadata_loader, cfg_version: sess.cfg_version, crate_name, @@ -610,32 +608,6 @@ impl<'a> CrateLocator<'a> { continue; } - // Ok so at this point we've determined that `(lib, kind)` above is - // a candidate crate to load, and that `slot` is either none (this - // is the first crate of its kind) or if some the previous path has - // the exact same hash (e.g., it's the exact same crate). - // - // In principle these two candidate crates are exactly the same so - // we can choose either of them to link. As a stupidly gross hack, - // however, we favor crate in the sysroot. - // - // You can find more info in rust-lang/rust#39518 and various linked - // issues, but the general gist is that during testing libstd the - // compilers has two candidates to choose from: one in the sysroot - // and one in the deps folder. These two crates are the exact same - // crate but if the compiler chooses the one in the deps folder - // it'll cause spurious errors on Windows. - // - // As a result, we favor the sysroot crate here. Note that the - // candidates are all canonicalized, so we canonicalize the sysroot - // as well. - if let Some((prev, _)) = &ret { - let sysroot = self.sysroot; - let sysroot = try_canonicalize(sysroot).unwrap_or_else(|_| sysroot.to_path_buf()); - if prev.starts_with(&sysroot) { - continue; - } - } *slot = Some((hash, metadata, lib.clone())); ret = Some((lib, kind)); } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 60cc138fd7bc2..a100e2d47bbbb 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -392,9 +392,6 @@ parse_invalid_identifier_with_leading_number = identifiers cannot start with a n parse_invalid_interpolated_expression = invalid interpolated expression -parse_invalid_literal_suffix = suffixes on {$kind} literals are invalid - .label = invalid suffix `{$suffix}` - parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid .label = invalid suffix `{$suffix}` .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases @@ -609,7 +606,6 @@ parse_nonterminal_expected_item_keyword = expected an item keyword parse_nonterminal_expected_lifetime = expected a lifetime, found `{$token}` parse_nonterminal_expected_statement = expected a statement -parse_not_supported = not supported parse_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 2f7ac7d3a12e5..de088b9364b26 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -667,7 +667,7 @@ impl<'a> Parser<'a> { { err.note("you may be trying to write a c-string literal"); err.note("c-string literals require Rust 2021 or later"); - HelpUseLatestEdition::new().add_to_diagnostic(&mut err); + err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new()); } // `pub` may be used for an item or `pub(crate)` diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index c223b8475288b..7fc523ffe0dea 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -302,9 +302,6 @@ passes_export_name = attribute should be applied to a free function, impl method or static .label = not a free function, impl method or static -passes_expr_not_allowed_in_context = - {$expr} is not allowed in a `{$context}` - passes_extern_main = the `main` function cannot be declared in an `extern` block @@ -405,8 +402,6 @@ passes_lang_item_on_incorrect_target = `{$name}` language item must be applied to a {$expected_target} .label = attribute should be applied to a {$expected_target}, not a {$actual_target} -passes_layout = - layout error: {$layout_error} passes_layout_abi = abi: {$abi} passes_layout_align = diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 1a54a2293573b..248a741af9079 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -17,10 +17,9 @@ use std::num::NonZero; use { parking_lot::{Condvar, Mutex}, rustc_data_structures::fx::FxHashSet, - rustc_data_structures::{defer, jobserver}, + rustc_data_structures::jobserver, rustc_span::DUMMY_SP, std::iter, - std::process, std::sync::Arc, }; @@ -514,12 +513,7 @@ fn remove_cycle( /// There may be multiple cycles involved in a deadlock, so this searches /// all active queries for cycles before finally resuming all the waiters at once. #[cfg(parallel_compiler)] -pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) { - let on_panic = defer(|| { - eprintln!("deadlock handler panicked, aborting process"); - process::abort(); - }); - +pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) { let mut wakelist = Vec::new(); let mut jobs: Vec = query_map.keys().cloned().collect(); @@ -539,19 +533,17 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) { // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here // only considers the true dependency and won't detect a cycle. if !found_cycle { - if query_map.len() == 0 { - panic!("deadlock detected without any query!") - } else { - panic!("deadlock detected! current query map:\n{:#?}", query_map); - } + panic!( + "deadlock detected as we're unable to find a query cycle to break\n\ + current query map:\n{:#?}", + query_map + ); } // FIXME: Ensure this won't cause a deadlock before we return for waiter in wakelist.into_iter() { waiter.notify(registry); } - - on_panic.disable(); } #[inline(never)] diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 0aefe553a3435..01b9d458f1e4f 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -3,7 +3,7 @@ pub use self::plumbing::*; mod job; #[cfg(parallel_compiler)] -pub use self::job::deadlock; +pub use self::job::break_query_cycles; pub use self::job::{ print_query_stack, report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, }; diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 616a7ccc7c64f..0c974ef4ca3eb 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -427,6 +427,17 @@ impl FileName { src.hash(&mut hasher); FileName::InlineAsm(hasher.finish()) } + + /// Returns the path suitable for reading from the file system on the local host, + /// if this information exists. + /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that. + pub fn into_local_path(self) -> Option { + match self { + FileName::Real(path) => path.into_local_path(), + FileName::DocTest(path, _) => Some(path), + _ => None, + } + } } /// Represents a span. diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index c35bab5ef6657..41adc2e79dc74 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -559,6 +559,30 @@ impl VecDeque { pub fn with_capacity(capacity: usize) -> VecDeque { Self::with_capacity_in(capacity, Global) } + + /// Creates an empty deque with space for at least `capacity` elements. + /// + /// # Errors + /// + /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, + /// or if the allocator reports allocation failure. + /// + /// # Examples + /// + /// ``` + /// # #![feature(try_with_capacity)] + /// # #[allow(unused)] + /// # fn example() -> Result<(), std::collections::TryReserveError> { + /// use std::collections::VecDeque; + /// + /// let deque: VecDeque = VecDeque::try_with_capacity(10)?; + /// # Ok(()) } + /// ``` + #[inline] + #[unstable(feature = "try_with_capacity", issue = "91913")] + pub fn try_with_capacity(capacity: usize) -> Result, TryReserveError> { + Ok(VecDeque { head: 0, len: 0, buf: RawVec::try_with_capacity_in(capacity, Global)? }) + } } impl VecDeque { diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index 61ec04a4849d0..0c7f94ccceb02 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -1,3 +1,5 @@ +//! [`CString`] and its related types. + #[cfg(test)] mod tests; diff --git a/library/alloc/src/ffi/mod.rs b/library/alloc/src/ffi/mod.rs index e8530fbc1f08f..9fc1acc231bff 100644 --- a/library/alloc/src/ffi/mod.rs +++ b/library/alloc/src/ffi/mod.rs @@ -80,9 +80,13 @@ #![stable(feature = "alloc_ffi", since = "1.64.0")] +#[doc(no_inline)] #[stable(feature = "alloc_c_string", since = "1.64.0")] -pub use self::c_str::FromVecWithNulError; +pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError}; + +#[doc(inline)] #[stable(feature = "alloc_c_string", since = "1.64.0")] -pub use self::c_str::{CString, IntoStringError, NulError}; +pub use self::c_str::CString; -mod c_str; +#[unstable(feature = "c_str_module", issue = "112134")] +pub mod c_str; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 28695ade5bf55..ca504b05a9689 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -163,6 +163,7 @@ #![feature(trusted_len)] #![feature(trusted_random_access)] #![feature(try_trait_v2)] +#![feature(try_with_capacity)] #![feature(tuple_trait)] #![feature(unchecked_math)] #![feature(unicode_internals)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 4d9694d4bebad..ace73c0fdaa5e 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -17,10 +17,19 @@ use crate::collections::TryReserveErrorKind::*; #[cfg(test)] mod tests; +// One central function responsible for reporting capacity overflows. This'll +// ensure that the code generation related to these panics is minimal as there's +// only one location which panics rather than a bunch throughout the module. #[cfg(not(no_global_oom_handling))] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +fn capacity_overflow() -> ! { + panic!("capacity overflow"); +} + enum AllocInit { /// The contents of the new memory are uninitialized. Uninitialized, + #[cfg(not(no_global_oom_handling))] /// The new memory is guaranteed to be zeroed. Zeroed, } @@ -93,6 +102,8 @@ impl RawVec { /// zero-sized. Note that if `T` is zero-sized this means you will /// *not* get a `RawVec` with the requested capacity. /// + /// Non-fallible version of `try_with_capacity` + /// /// # Panics /// /// Panics if the requested capacity exceeds `isize::MAX` bytes. @@ -104,7 +115,7 @@ impl RawVec { #[must_use] #[inline] pub fn with_capacity(capacity: usize) -> Self { - Self::with_capacity_in(capacity, Global) + handle_reserve(Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global)) } /// Like `with_capacity`, but guarantees the buffer is zeroed. @@ -142,7 +153,14 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { - Self::allocate_in(capacity, AllocInit::Uninitialized, alloc) + handle_reserve(Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)) + } + + /// Like `try_with_capacity`, but parameterized over the choice of + /// allocator for the returned `RawVec`. + #[inline] + pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc) } /// Like `with_capacity_zeroed`, but parameterized over the choice @@ -150,7 +168,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { - Self::allocate_in(capacity, AllocInit::Zeroed, alloc) + handle_reserve(Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc)) } /// Converts the entire buffer into `Box<[MaybeUninit]>` with the specified `len`. @@ -179,35 +197,41 @@ impl RawVec { } } - #[cfg(not(no_global_oom_handling))] - fn allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Self { + fn try_allocate_in( + capacity: usize, + init: AllocInit, + alloc: A, + ) -> Result { // Don't allocate here because `Drop` will not deallocate when `capacity` is 0. + if T::IS_ZST || capacity == 0 { - Self::new_in(alloc) + Ok(Self::new_in(alloc)) } else { // We avoid `unwrap_or_else` here because it bloats the amount of // LLVM IR generated. let layout = match Layout::array::(capacity) { Ok(layout) => layout, - Err(_) => capacity_overflow(), + Err(_) => return Err(CapacityOverflow.into()), }; - match alloc_guard(layout.size()) { - Ok(_) => {} - Err(_) => capacity_overflow(), + + if let Err(err) = alloc_guard(layout.size()) { + return Err(err); } + let result = match init { AllocInit::Uninitialized => alloc.allocate(layout), + #[cfg(not(no_global_oom_handling))] AllocInit::Zeroed => alloc.allocate_zeroed(layout), }; let ptr = match result { Ok(ptr) => ptr, - Err(_) => handle_alloc_error(layout), + Err(_) => return Err(AllocError { layout, non_exhaustive: () }.into()), }; // Allocators currently return a `NonNull<[u8]>` whose length // matches the size requested. If that ever changes, the capacity // here should change to `ptr.len() / mem::size_of::()`. - Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc } + Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) } } @@ -537,11 +561,11 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { // Central function for reserve error handling. #[cfg(not(no_global_oom_handling))] #[inline] -fn handle_reserve(result: Result<(), TryReserveError>) { +fn handle_reserve(result: Result) -> T { match result.map_err(|e| e.kind()) { + Ok(res) => res, Err(CapacityOverflow) => capacity_overflow(), Err(AllocError { layout, .. }) => handle_alloc_error(layout), - Ok(()) => { /* yay */ } } } @@ -561,12 +585,3 @@ fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { Ok(()) } } - -// One central function responsible for reporting capacity overflows. This'll -// ensure that the code generation related to these panics is minimal as there's -// only one location which panics rather than a bunch throughout the module. -#[cfg(not(no_global_oom_handling))] -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] -fn capacity_overflow() -> ! { - panic!("capacity overflow"); -} diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index f8cada01c0309..4194be530612d 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -105,13 +105,14 @@ fn zst() { let v: RawVec = RawVec::with_capacity_in(100, Global); zst_sanity(&v); - let v: RawVec = RawVec::allocate_in(0, AllocInit::Uninitialized, Global); + let v: RawVec = RawVec::try_allocate_in(0, AllocInit::Uninitialized, Global).unwrap(); zst_sanity(&v); - let v: RawVec = RawVec::allocate_in(100, AllocInit::Uninitialized, Global); + let v: RawVec = RawVec::try_allocate_in(100, AllocInit::Uninitialized, Global).unwrap(); zst_sanity(&v); - let mut v: RawVec = RawVec::allocate_in(usize::MAX, AllocInit::Uninitialized, Global); + let mut v: RawVec = + RawVec::try_allocate_in(usize::MAX, AllocInit::Uninitialized, Global).unwrap(); zst_sanity(&v); // Check all these operations work as expected with zero-sized elements. diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 98ded7f6cdf5b..c4dcff1b1c49c 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -492,6 +492,19 @@ impl String { String { vec: Vec::with_capacity(capacity) } } + /// Creates a new empty `String` with at least the specified capacity. + /// + /// # Errors + /// + /// Returns [`Err`] if the capacity exceeds `isize::MAX` bytes, + /// or if the memory allocator reports failure. + /// + #[inline] + #[unstable(feature = "try_with_capacity", issue = "91913")] + pub fn try_with_capacity(capacity: usize) -> Result { + Ok(String { vec: Vec::try_with_capacity(capacity)? }) + } + // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is // required for this method definition, is not available. Since we don't // require this method for testing purposes, I'll just stub it diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index c0e934b3b1fcb..f2f42e63d6b0b 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -481,6 +481,22 @@ impl Vec { Self::with_capacity_in(capacity, Global) } + /// Constructs a new, empty `Vec` with at least the specified capacity. + /// + /// The vector will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is 0, the vector will not allocate. + /// + /// # Errors + /// + /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, + /// or if the allocator reports allocation failure. + #[inline] + #[unstable(feature = "try_with_capacity", issue = "91913")] + pub fn try_with_capacity(capacity: usize) -> Result { + Self::try_with_capacity_in(capacity, Global) + } + /// Creates a `Vec` directly from a pointer, a length, and a capacity. /// /// # Safety @@ -672,6 +688,24 @@ impl Vec { Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 } } + /// Constructs a new, empty `Vec` with at least the specified capacity + /// with the provided allocator. + /// + /// The vector will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is 0, the vector will not allocate. + /// + /// # Errors + /// + /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, + /// or if the allocator reports allocation failure. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "try_with_capacity", issue = "91913")] + pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + Ok(Vec { buf: RawVec::try_with_capacity_in(capacity, alloc)?, len: 0 }) + } + /// Creates a `Vec` directly from a pointer, a length, a capacity, /// and an allocator. /// diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index ed928994ad697..e8496989bcf12 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -20,6 +20,7 @@ #![feature(pattern)] #![feature(trusted_len)] #![feature(try_reserve_kind)] +#![feature(try_with_capacity)] #![feature(unboxed_closures)] #![feature(associated_type_bounds)] #![feature(binary_heap_into_iter_sorted)] diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index 711e4eef2e724..e20ceae87b0d5 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -723,6 +723,17 @@ fn test_reserve_exact() { assert!(s.capacity() >= 33) } +#[test] +#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc +fn test_try_with_capacity() { + let string = String::try_with_capacity(1000).unwrap(); + assert_eq!(0, string.len()); + assert!(string.capacity() >= 1000 && string.capacity() <= isize::MAX as usize); + + assert!(String::try_with_capacity(usize::MAX).is_err()); +} + #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM #[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 15ee4d6520523..aa95b4e977081 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1694,6 +1694,18 @@ fn test_reserve_exact() { assert!(v.capacity() >= 33) } +#[test] +#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc +fn test_try_with_capacity() { + let mut vec: Vec = Vec::try_with_capacity(5).unwrap(); + assert_eq!(0, vec.len()); + assert!(vec.capacity() >= 5 && vec.capacity() <= isize::MAX as usize / 4); + assert!(vec.spare_capacity_mut().len() >= 5); + + assert!(Vec::::try_with_capacity(isize::MAX as usize + 1).is_err()); +} + #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM #[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index eda2f8bb812b5..cea5de4dd5984 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1182,6 +1182,17 @@ fn test_reserve_exact_2() { assert!(v.capacity() >= 33) } +#[test] +#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc +fn test_try_with_capacity() { + let vec: VecDeque = VecDeque::try_with_capacity(5).unwrap(); + assert_eq!(0, vec.len()); + assert!(vec.capacity() >= 5 && vec.capacity() <= isize::MAX as usize / 4); + + assert!(VecDeque::::try_with_capacity(isize::MAX as usize + 1).is_err()); +} + #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM #[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 0825281090cd1..111fb83088b82 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -1,3 +1,5 @@ +//! [`CStr`] and its related types. + use crate::cmp::Ordering; use crate::error::Error; use crate::ffi::c_char; @@ -9,15 +11,20 @@ use crate::slice; use crate::slice::memchr; use crate::str; +// FIXME: because this is doc(inline)d, we *have* to use intra-doc links because the actual link +// depends on where the item is being documented. however, since this is libcore, we can't +// actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the +// links to `CString` and `String` for now until a solution is developed + /// Representation of a borrowed C string. /// /// This type represents a borrowed reference to a nul-terminated /// array of bytes. It can be constructed safely from a &[[u8]] /// slice, or unsafely from a raw `*const c_char`. It can then be /// converted to a Rust &[str] by performing UTF-8 validation, or -/// into an owned [`CString`]. +/// into an owned `CString`. /// -/// `&CStr` is to [`CString`] as &[str] is to [`String`]: the former +/// `&CStr` is to `CString` as &[str] is to `String`: the former /// in each pair are borrowed references; the latter are owned /// strings. /// @@ -26,9 +33,6 @@ use crate::str; /// Instead, safe wrappers of FFI functions may leverage the unsafe [`CStr::from_ptr`] constructor /// to provide a safe interface to other consumers. /// -/// [`CString`]: ../../std/ffi/struct.CString.html -/// [`String`]: ../../std/string/struct.String.html -/// /// # Examples /// /// Inspecting a foreign C string: @@ -125,10 +129,13 @@ enum FromBytesWithNulErrorKind { NotNulTerminated, } +// FIXME: const stability attributes should not be required here, I think impl FromBytesWithNulError { + #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] const fn interior_nul(pos: usize) -> FromBytesWithNulError { FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) } } + #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] const fn not_nul_terminated() -> FromBytesWithNulError { FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated } } diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 44200926a32eb..3627e844222ac 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -13,10 +13,20 @@ use crate::fmt; use crate::marker::PhantomData; use crate::ops::{Deref, DerefMut}; +#[doc(no_inline)] #[stable(feature = "core_c_str", since = "1.64.0")] -pub use self::c_str::{CStr, FromBytesUntilNulError, FromBytesWithNulError}; +pub use self::c_str::FromBytesWithNulError; -mod c_str; +#[doc(no_inline)] +#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] +pub use self::c_str::FromBytesUntilNulError; + +#[doc(inline)] +#[stable(feature = "core_c_str", since = "1.64.0")] +pub use self::c_str::CStr; + +#[unstable(feature = "c_str_module", issue = "112134")] +pub mod c_str; macro_rules! type_alias { { diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index c19b5791562ce..026e21586d403 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1125,22 +1125,6 @@ impl MaybeUninit { // unlike copy_from_slice this does not call clone_from_slice on the slice // this is because `MaybeUninit` does not implement Clone. - struct Guard<'a, T> { - slice: &'a mut [MaybeUninit], - initialized: usize, - } - - impl<'a, T> Drop for Guard<'a, T> { - fn drop(&mut self) { - let initialized_part = &mut self.slice[..self.initialized]; - // SAFETY: this raw slice will contain only initialized objects - // that's why, it is allowed to drop it. - unsafe { - crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); - } - } - } - assert_eq!(this.len(), src.len(), "destination and source slices have different lengths"); // NOTE: We need to explicitly slice them to the same length // for bounds checking to be elided, and the optimizer will @@ -1162,6 +1146,151 @@ impl MaybeUninit { unsafe { MaybeUninit::slice_assume_init_mut(this) } } + /// Fills `this` with elements by cloning `value`, returning a mutable reference to the now + /// initialized contents of `this`. + /// Any previously initialized elements will not be dropped. + /// + /// This is similar to [`slice::fill`]. + /// + /// # Panics + /// + /// This function will panic if any call to `Clone` panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// Fill an uninit vec with 1. + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = vec![MaybeUninit::uninit(); 10]; + /// let initialized = MaybeUninit::fill(buf.as_mut_slice(), 1); + /// assert_eq!(initialized, &mut [1; 10]); + /// ``` + #[doc(alias = "memset")] + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn fill<'a>(this: &'a mut [MaybeUninit], value: T) -> &'a mut [T] + where + T: Clone, + { + SpecFill::spec_fill(this, value); + // SAFETY: Valid elements have just been filled into `this` so it is initialized + unsafe { MaybeUninit::slice_assume_init_mut(this) } + } + + /// Fills `this` with elements returned by calling a closure repeatedly. + /// + /// This method uses a closure to create new values. If you'd rather `Clone` a given value, use + /// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can + /// pass [`Default::default`] as the argument. + /// + /// # Panics + /// + /// This function will panic if any call to the provided closure panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// Fill an uninit vec with the default value. + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = vec![MaybeUninit::::uninit(); 10]; + /// let initialized = MaybeUninit::fill_with(buf.as_mut_slice(), Default::default); + /// assert_eq!(initialized, &mut [0; 10]); + /// ``` + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit], mut f: F) -> &'a mut [T] + where + F: FnMut() -> T, + { + let mut guard = Guard { slice: this, initialized: 0 }; + + for element in guard.slice.iter_mut() { + element.write(f()); + guard.initialized += 1; + } + + super::forget(guard); + + // SAFETY: Valid elements have just been written into `this` so it is initialized + unsafe { MaybeUninit::slice_assume_init_mut(this) } + } + + /// Fills `this` with elements yielded by an iterator until either all elements have been + /// initialized or the iterator is empty. + /// + /// Returns two slices. The first slice contains the initialized portion of the original slice. + /// The second slice is the still-uninitialized remainder of the original slice. + /// + /// # Panics + /// + /// This function panics if the iterator's `next` function panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// Fill an uninit vec with a cycling iterator. + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = vec![MaybeUninit::uninit(); 5]; + /// + /// let iter = [1, 2, 3].into_iter().cycle(); + /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); + /// + /// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]); + /// assert_eq!(0, remainder.len()); + /// ``` + /// + /// Fill an uninit vec, but not completely. + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = vec![MaybeUninit::uninit(); 5]; + /// let iter = [1, 2]; + /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); + /// + /// assert_eq!(initialized, &mut [1, 2]); + /// assert_eq!(remainder.len(), 3); + /// ``` + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn fill_from<'a, I>( + this: &'a mut [MaybeUninit], + it: I, + ) -> (&'a mut [T], &'a mut [MaybeUninit]) + where + I: IntoIterator, + { + let iter = it.into_iter(); + let mut guard = Guard { slice: this, initialized: 0 }; + + for (element, val) in guard.slice.iter_mut().zip(iter) { + element.write(val); + guard.initialized += 1; + } + + let initialized_len = guard.initialized; + super::forget(guard); + + // SAFETY: guard.initialized <= this.len() + let (initted, remainder) = unsafe { this.split_at_mut_unchecked(initialized_len) }; + + // SAFETY: Valid elements have just been written into `init`, so that portion + // of `this` is initialized. + (unsafe { MaybeUninit::slice_assume_init_mut(initted) }, remainder) + } + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still @@ -1315,3 +1444,44 @@ impl [MaybeUninit; N] { unsafe { intrinsics::transmute_unchecked(self) } } } + +struct Guard<'a, T> { + slice: &'a mut [MaybeUninit], + initialized: usize, +} + +impl<'a, T> Drop for Guard<'a, T> { + fn drop(&mut self) { + let initialized_part = &mut self.slice[..self.initialized]; + // SAFETY: this raw sub-slice will contain only initialized objects. + unsafe { + crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); + } + } +} + +trait SpecFill { + fn spec_fill(&mut self, value: T); +} + +impl SpecFill for [MaybeUninit] { + default fn spec_fill(&mut self, value: T) { + let mut guard = Guard { slice: self, initialized: 0 }; + + if let Some((last, elems)) = guard.slice.split_last_mut() { + for el in elems { + el.write(value.clone()); + guard.initialized += 1; + } + + last.write(value); + } + super::forget(guard); + } +} + +impl SpecFill for [MaybeUninit] { + fn spec_fill(&mut self, value: T) { + self.fill(MaybeUninit::new(value)); + } +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index fa0e9a979d060..c5a7e87c4aa4f 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -54,6 +54,7 @@ #![feature(slice_from_ptr_range)] #![feature(slice_split_once)] #![feature(split_as_slice)] +#![feature(maybe_uninit_fill)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_write_slice)] #![feature(maybe_uninit_uninit_array_transpose)] diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index 0f7fde747690a..e388800f400df 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -308,21 +308,226 @@ fn uninit_write_slice_cloned_mid_panic() { } } +#[derive(Clone)] +struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + panic!("dropped a bomb! kaboom!") + } +} + #[test] fn uninit_write_slice_cloned_no_drop() { - #[derive(Clone)] - struct Bomb; + let mut dst = [MaybeUninit::uninit()]; + let src = [Bomb]; + + MaybeUninit::clone_from_slice(&mut dst, &src); + + forget(src); +} + +#[test] +fn uninit_fill() { + let mut dst = [MaybeUninit::new(255); 64]; + let expect = [0; 64]; + + assert_eq!(MaybeUninit::fill(&mut dst, 0), &expect); +} + +#[cfg(panic = "unwind")] +struct CloneUntilPanic { + limit: usize, + rc: Rc<()>, +} - impl Drop for Bomb { - fn drop(&mut self) { - panic!("dropped a bomb! kaboom") +#[cfg(panic = "unwind")] +impl Clone for CloneUntilPanic { + fn clone(&self) -> Self { + if Rc::strong_count(&self.rc) >= self.limit { + panic!("expected panic on clone"); } + Self { limit: self.limit, rc: self.rc.clone() } } +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_clone_panic_drop() { + use std::panic; + + let rc = Rc::new(()); + + let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; + + let src = CloneUntilPanic { limit: 3, rc: rc.clone() }; + let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { + MaybeUninit::fill(&mut dst, src); + })); + + match err { + Ok(_) => unreachable!(), + Err(payload) => { + payload + .downcast::<&'static str>() + .and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) }) + .unwrap_or_else(|p| panic::resume_unwind(p)); + assert_eq!(Rc::strong_count(&rc), 1) + } + } +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_clone_no_drop_clones() { + let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; + + MaybeUninit::fill(&mut dst, Bomb); +} + +#[test] +fn uninit_fill_with() { + let mut dst = [MaybeUninit::new(255); 64]; + let expect = [0; 64]; + + assert_eq!(MaybeUninit::fill_with(&mut dst, || 0), &expect); +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_with_mid_panic() { + use std::panic; + + let rc = Rc::new(()); + + let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; + + let src = CloneUntilPanic { limit: 3, rc: rc.clone() }; + let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { + MaybeUninit::fill_with(&mut dst, || src.clone()); + })); + + drop(src); + + match err { + Ok(_) => unreachable!(), + Err(payload) => { + payload + .downcast::<&'static str>() + .and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) }) + .unwrap_or_else(|p| panic::resume_unwind(p)); + assert_eq!(Rc::strong_count(&rc), 1) + } + } +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_with_no_drop() { + let mut dst = [MaybeUninit::uninit()]; + let src = Bomb; + + MaybeUninit::fill_with(&mut dst, || src.clone()); + + forget(src); +} + +#[test] +fn uninit_fill_from() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 64]; + + let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + assert_eq!(initted, &src); + assert_eq!(remainder.len(), 0); +} + +#[test] +fn uninit_fill_from_partial() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 48]; + + let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + assert_eq!(initted, &src); + assert_eq!(remainder.len(), 16); +} + +#[test] +fn uninit_over_fill() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 72]; + + let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + assert_eq!(initted, &src[0..64]); + assert_eq!(remainder.len(), 0); +} + +#[test] +fn uninit_empty_fill() { + let mut dst = [MaybeUninit::new(255); 64]; + let src = [0; 0]; + + let (initted, remainder) = MaybeUninit::fill_from(&mut dst, src.into_iter()); + assert_eq!(initted, &src[0..0]); + assert_eq!(remainder.len(), 64); +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_from_mid_panic() { + use std::panic; + + struct IterUntilPanic { + limit: usize, + rc: Rc<()>, + } + + impl Iterator for IterUntilPanic { + type Item = Rc<()>; + fn next(&mut self) -> Option { + if Rc::strong_count(&self.rc) >= self.limit { + panic!("expected panic on next"); + } + Some(self.rc.clone()) + } + } + + let rc = Rc::new(()); + + let mut dst = [ + MaybeUninit::uninit(), + MaybeUninit::uninit(), + MaybeUninit::uninit(), + MaybeUninit::uninit(), + ]; + + let src = IterUntilPanic { limit: 3, rc: rc.clone() }; + + let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { + MaybeUninit::fill_from(&mut dst, src); + })); + + match err { + Ok(_) => unreachable!(), + Err(payload) => { + payload + .downcast::<&'static str>() + .and_then(|s| if *s == "expected panic on next" { Ok(s) } else { Err(s) }) + .unwrap_or_else(|p| panic::resume_unwind(p)); + + assert_eq!(Rc::strong_count(&rc), 1) + } + } +} + +#[test] +#[cfg(panic = "unwind")] +fn uninit_fill_from_no_drop() { let mut dst = [MaybeUninit::uninit()]; let src = [Bomb]; - MaybeUninit::clone_from_slice(&mut dst, &src); + MaybeUninit::fill_from(&mut dst, src.iter()); forget(src); } diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs new file mode 100644 index 0000000000000..b59b0c5bba65a --- /dev/null +++ b/library/std/src/ffi/c_str.rs @@ -0,0 +1,19 @@ +//! [`CStr`], [`CString`], and related types. + +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::ffi::c_str::CStr; + +#[stable(feature = "cstr_from_bytes", since = "1.10.0")] +pub use core::ffi::c_str::FromBytesWithNulError; + +#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] +pub use core::ffi::c_str::FromBytesUntilNulError; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc::ffi::c_str::{CString, NulError}; + +#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] +pub use alloc::ffi::c_str::FromVecWithNulError; + +#[stable(feature = "cstring_into", since = "1.7.0")] +pub use alloc::ffi::c_str::IntoStringError; diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index f810611a02ecb..f45fd77e8b167 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -161,12 +161,32 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[stable(feature = "alloc_c_string", since = "1.64.0")] -pub use alloc::ffi::{CString, FromVecWithNulError, IntoStringError, NulError}; -#[stable(feature = "cstr_from_bytes_until_nul", since = "1.73.0")] -pub use core::ffi::FromBytesUntilNulError; -#[stable(feature = "core_c_str", since = "1.64.0")] -pub use core::ffi::{CStr, FromBytesWithNulError}; +#[unstable(feature = "c_str_module", issue = "112134")] +pub mod c_str; + +#[doc(inline)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::c_str::{CStr, CString}; + +#[doc(no_inline)] +#[stable(feature = "cstr_from_bytes", since = "1.10.0")] +pub use self::c_str::FromBytesWithNulError; + +#[doc(no_inline)] +#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] +pub use self::c_str::FromBytesUntilNulError; + +#[doc(no_inline)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::c_str::NulError; + +#[doc(no_inline)] +#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] +pub use self::c_str::FromVecWithNulError; + +#[doc(no_inline)] +#[stable(feature = "cstring_into", since = "1.7.0")] +pub use self::c_str::IntoStringError; #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 42fc0053030af..ccc2ed916884f 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -453,6 +453,32 @@ impl Read for Stdin { } } +#[stable(feature = "read_shared_stdin", since = "CURRENT_RUSTC_VERSION")] +impl Read for &Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.lock().read(buf) + } + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.lock().read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.lock().is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.lock().read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.lock().read_to_string(buf) + } + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.lock().read_exact(buf) + } +} + // only used by platform-dependent io::copy specializations, i.e. unused on some platforms #[cfg(any(target_os = "linux", target_os = "android"))] impl StdinLock<'_> { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 55a5292a4a41b..8cf44f4760dae 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -314,6 +314,7 @@ // // Library features (core): // tidy-alphabetical-start +#![feature(c_str_module)] #![feature(char_internals)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile index fe30a95344104..1eedfe3f09b4d 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile @@ -42,6 +42,10 @@ RUN sh /scripts/sccache.sh ENV NO_DOWNLOAD_CI_LLVM 1 ENV EXTERNAL_LLVM 1 +# This is not the latest LLVM version, so some components required by tests may +# be missing. +ENV IS_NOT_LATEST_LLVM 1 + # Using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile new file mode 100644 index 0000000000000..e8383500dfc94 --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -0,0 +1,55 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + gcc-multilib \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + llvm-18-tools \ + llvm-18-dev \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + xz-utils \ + nodejs \ + mingw-w64 \ + libgccjit-13-dev \ + && rm -rf /var/lib/apt/lists/* + +# Note: libgccjit needs to match the default gcc version for the linker to find it. + +# Install powershell (universal package) so we can test x.ps1 on Linux +# FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. +RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ + dpkg --ignore-depends=libicu72 -i powershell.deb && \ + rm -f powershell.deb + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# We are disabling CI LLVM since this builder is intentionally using a host +# LLVM, rather than the typical src/llvm-project LLVM. +ENV NO_DOWNLOAD_CI_LLVM 1 +ENV EXTERNAL_LLVM 1 + +# Using llvm-link-shared due to libffi issues -- see #34486 +ENV RUST_CONFIGURE_ARGS \ + --build=x86_64-unknown-linux-gnu \ + --llvm-root=/usr/lib/llvm-18 \ + --enable-llvm-link-shared \ + --set rust.thin-lto-import-instr-limit=10 + +COPY host-x86_64/x86_64-gnu-llvm-16/script.sh /tmp/ + +ENV SCRIPT /tmp/script.sh diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 2ba5d357a1d00..c392ccec3dc2b 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -510,6 +510,11 @@ jobs: - name: x86_64-gnu-distcheck <<: *job-linux-8c + - name: x86_64-gnu-llvm-18 + env: + RUST_BACKTRACE: 1 + <<: *job-linux-8c + - name: x86_64-gnu-llvm-17 env: RUST_BACKTRACE: 1 diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs index 67b745373f019..9803b6eab2db5 100644 --- a/src/tools/tidy/src/fluent_alphabetical.rs +++ b/src/tools/tidy/src/fluent_alphabetical.rs @@ -1,6 +1,7 @@ //! Checks that all Flunt files have messages in alphabetical order use crate::walk::{filter_dirs, walk}; +use std::collections::HashMap; use std::{fs::OpenOptions, io::Write, path::Path}; use regex::Regex; @@ -13,11 +14,27 @@ fn filter_fluent(path: &Path) -> bool { if let Some(ext) = path.extension() { ext.to_str() != Some("ftl") } else { true } } -fn check_alphabetic(filename: &str, fluent: &str, bad: &mut bool) { +fn check_alphabetic( + filename: &str, + fluent: &str, + bad: &mut bool, + all_defined_msgs: &mut HashMap, +) { let mut matches = MESSAGE.captures_iter(fluent).peekable(); while let Some(m) = matches.next() { + let name = m.get(1).unwrap(); + if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) { + tidy_error!( + bad, + "{filename}: message `{}` is already defined in {}", + name.as_str(), + defined_filename, + ); + } + + all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned()); + if let Some(next) = matches.peek() { - let name = m.get(1).unwrap(); let next = next.get(1).unwrap(); if name.as_str() > next.as_str() { tidy_error!( @@ -34,13 +51,29 @@ run `./x.py test tidy --bless` to sort the file correctly", } } -fn sort_messages(fluent: &str) -> String { +fn sort_messages( + filename: &str, + fluent: &str, + bad: &mut bool, + all_defined_msgs: &mut HashMap, +) -> String { let mut chunks = vec![]; let mut cur = String::new(); for line in fluent.lines() { - if MESSAGE.is_match(line) { + if let Some(name) = MESSAGE.find(line) { + if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) { + tidy_error!( + bad, + "{filename}: message `{}` is already defined in {}", + name.as_str(), + defined_filename, + ); + } + + all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned()); chunks.push(std::mem::take(&mut cur)); } + cur += line; cur.push('\n'); } @@ -53,20 +86,33 @@ fn sort_messages(fluent: &str) -> String { } pub fn check(path: &Path, bless: bool, bad: &mut bool) { + let mut all_defined_msgs = HashMap::new(); walk( path, |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)), &mut |ent, contents| { if bless { - let sorted = sort_messages(contents); + let sorted = sort_messages( + ent.path().to_str().unwrap(), + contents, + bad, + &mut all_defined_msgs, + ); if sorted != contents { let mut f = OpenOptions::new().write(true).truncate(true).open(ent.path()).unwrap(); f.write(sorted.as_bytes()).unwrap(); } } else { - check_alphabetic(ent.path().to_str().unwrap(), contents, bad); + check_alphabetic( + ent.path().to_str().unwrap(), + contents, + bad, + &mut all_defined_msgs, + ); } }, ); + + crate::fluent_used::check(path, all_defined_msgs, bad); } diff --git a/src/tools/tidy/src/fluent_used.rs b/src/tools/tidy/src/fluent_used.rs new file mode 100644 index 0000000000000..b73e79cb38d94 --- /dev/null +++ b/src/tools/tidy/src/fluent_used.rs @@ -0,0 +1,43 @@ +//! Checks that all Fluent messages appear at least twice + +use crate::walk::{filter_dirs, walk}; +use regex::Regex; +use std::collections::HashMap; +use std::path::Path; + +lazy_static::lazy_static! { + static ref WORD: Regex = Regex::new(r"\w+").unwrap(); +} + +fn filter_used_messages( + contents: &str, + msgs_not_appeared_yet: &mut HashMap, + msgs_appeared_only_once: &mut HashMap, +) { + // we don't just check messages never appear in Rust files, + // because messages can be used as parts of other fluent messages in Fluent files, + // so we do checking messages appear only once in all Rust and Fluent files. + let mut matches = WORD.find_iter(contents); + while let Some(name) = matches.next() { + if let Some((name, filename)) = msgs_not_appeared_yet.remove_entry(name.as_str()) { + // if one msg appears for the first time, + // remove it from `msgs_not_appeared_yet` and insert it into `msgs_appeared_only_once`. + msgs_appeared_only_once.insert(name, filename); + } else { + // if one msg appears for the second time, + // remove it from `msgs_appeared_only_once`. + msgs_appeared_only_once.remove(name.as_str()); + } + } +} + +pub fn check(path: &Path, mut all_defined_msgs: HashMap, bad: &mut bool) { + let mut msgs_appear_only_once = HashMap::new(); + walk(path, |path, _| filter_dirs(path), &mut |_, contents| { + filter_used_messages(contents, &mut all_defined_msgs, &mut msgs_appear_only_once); + }); + + for (name, filename) in msgs_appear_only_once { + tidy_error!(bad, "{filename}: message `{}` is not used", name,); + } +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 6f3ade0ab58c7..670b7eb2be995 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -65,6 +65,7 @@ pub mod ext_tool_checks; pub mod extdeps; pub mod features; pub mod fluent_alphabetical; +mod fluent_used; pub(crate) mod iter_header; pub mod mir_opt_tests; pub mod pal; diff --git a/tests/codegen/vec-with-capacity.rs b/tests/codegen/vec-with-capacity.rs new file mode 100644 index 0000000000000..47051f2eef891 --- /dev/null +++ b/tests/codegen/vec-with-capacity.rs @@ -0,0 +1,35 @@ +//@ compile-flags: -O +//@ ignore-debug +// (with debug assertions turned on, `assert_unchecked` generates a real assertion) + +#![crate_type = "lib"] +#![feature(try_with_capacity)] + +// CHECK-LABEL: @with_capacity_does_not_grow1 +#[no_mangle] +pub fn with_capacity_does_not_grow1() -> Vec { + let v = Vec::with_capacity(1234); + // CHECK: call {{.*}}__rust_alloc( + // CHECK-NOT: call {{.*}}__rust_realloc + // CHECK-NOT: call {{.*}}capacity_overflow + // CHECK-NOT: call {{.*}}finish_grow + // CHECK-NOT: call {{.*}}reserve + // CHECK-NOT: memcpy + // CHECK-NOT: memset + v +} + +// CHECK-LABEL: @try_with_capacity_does_not_grow2 +#[no_mangle] +pub fn try_with_capacity_does_not_grow2() -> Option>> { + let v = Vec::try_with_capacity(1234).ok()?; + // CHECK: call {{.*}}__rust_alloc( + // CHECK-NOT: call {{.*}}__rust_realloc + // CHECK-NOT: call {{.*}}capacity_overflow + // CHECK-NOT: call {{.*}}finish_grow + // CHECK-NOT: call {{.*}}handle_alloc_error + // CHECK-NOT: call {{.*}}reserve + // CHECK-NOT: memcpy + // CHECK-NOT: memset + Some(v) +} diff --git a/tests/ui/hygiene/panic-location.run.stderr b/tests/ui/hygiene/panic-location.run.stderr index 5c552411da7f3..ec0ce18c3dfa7 100644 --- a/tests/ui/hygiene/panic-location.run.stderr +++ b/tests/ui/hygiene/panic-location.run.stderr @@ -1,3 +1,3 @@ -thread 'main' panicked at library/alloc/src/raw_vec.rs:571:5: +thread 'main' panicked at library/alloc/src/raw_vec.rs:26:5: capacity overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/tests/ui/include-macros/parent_dir.rs b/tests/ui/include-macros/parent_dir.rs new file mode 100644 index 0000000000000..321baf77025d2 --- /dev/null +++ b/tests/ui/include-macros/parent_dir.rs @@ -0,0 +1,12 @@ +//@ normalize-stderr-test: "`: .*\(os error" -> "`: $$FILE_NOT_FOUND_MSG (os error" + +fn main() { + let _ = include_str!("include-macros/file.txt"); //~ ERROR couldn't read + //~^HELP different directory + let _ = include_str!("hello.rs"); //~ ERROR couldn't read + //~^HELP different directory + let _ = include_bytes!("../../data.bin"); //~ ERROR couldn't read + //~^HELP different directory + let _ = include_str!("tests/ui/include-macros/file.txt"); //~ ERROR couldn't read + //~^HELP different directory +} diff --git a/tests/ui/include-macros/parent_dir.stderr b/tests/ui/include-macros/parent_dir.stderr new file mode 100644 index 0000000000000..7610360815b7a --- /dev/null +++ b/tests/ui/include-macros/parent_dir.stderr @@ -0,0 +1,50 @@ +error: couldn't read `$DIR/include-macros/file.txt`: $FILE_NOT_FOUND_MSG (os error 2) + --> $DIR/parent_dir.rs:4:13 + | +LL | let _ = include_str!("include-macros/file.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_str!("file.txt"); + | ~~~~~~~~~~ + +error: couldn't read `$DIR/hello.rs`: $FILE_NOT_FOUND_MSG (os error 2) + --> $DIR/parent_dir.rs:6:13 + | +LL | let _ = include_str!("hello.rs"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_str!("../hello.rs"); + | ~~~~~~~~~~~~~ + +error: couldn't read `$DIR/../../data.bin`: $FILE_NOT_FOUND_MSG (os error 2) + --> $DIR/parent_dir.rs:8:13 + | +LL | let _ = include_bytes!("../../data.bin"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_bytes!("data.bin"); + | ~~~~~~~~~~ + +error: couldn't read `$DIR/tests/ui/include-macros/file.txt`: $FILE_NOT_FOUND_MSG (os error 2) + --> $DIR/parent_dir.rs:10:13 + | +LL | let _ = include_str!("tests/ui/include-macros/file.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_str!("file.txt"); + | ~~~~~~~~~~ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/lint/non_local_definitions.rs b/tests/ui/lint/non_local_definitions.rs index bbcc81898713c..eee582a6f11ba 100644 --- a/tests/ui/lint/non_local_definitions.rs +++ b/tests/ui/lint/non_local_definitions.rs @@ -1,7 +1,7 @@ //@ check-pass //@ edition:2021 //@ aux-build:non_local_macro.rs -//@ rustc-env:CARGO=/usr/bin/cargo +//@ rustc-env:CARGO_CRATE_NAME=non_local_def #![feature(inline_const)] #![warn(non_local_definitions)] @@ -245,6 +245,26 @@ fn bad() { //~^ WARN non-local `impl` definition } +trait Uto9 {} +trait Uto10 {} +const _: u32 = { + let _a = || { + impl Uto9 for Test {} + //~^ WARN non-local `impl` definition + + 1 + }; + + type A = [u32; { + impl Uto10 for Test {} + //~^ WARN non-local `impl` definition + + 1 + }]; + + 1 +}; + struct UwU(T); fn fun() { diff --git a/tests/ui/lint/non_local_definitions.stderr b/tests/ui/lint/non_local_definitions.stderr index b9583ae983f34..ef74e262f9dbc 100644 --- a/tests/ui/lint/non_local_definitions.stderr +++ b/tests/ui/lint/non_local_definitions.stderr @@ -442,7 +442,29 @@ LL | impl Uto8 for T {} = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:253:5 + --> $DIR/non_local_definitions.rs:252:9 + | +LL | impl Uto9 for Test {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: move this `impl` block outside the of the current closure `` and up 2 bodies + = note: an `impl` definition is non-local if it is nested inside an item and neither the type nor the trait are at the same nesting level as the `impl` block + = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue + +warning: non-local `impl` definition, they should be avoided as they go against expectation + --> $DIR/non_local_definitions.rs:259:9 + | +LL | impl Uto10 for Test {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: move this `impl` block outside the of the current constant expression `` and up 2 bodies + = note: an `impl` definition is non-local if it is nested inside an item and neither the type nor the trait are at the same nesting level as the `impl` block + = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue + +warning: non-local `impl` definition, they should be avoided as they go against expectation + --> $DIR/non_local_definitions.rs:273:5 | LL | / impl Default for UwU { LL | | @@ -458,7 +480,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:264:5 + --> $DIR/non_local_definitions.rs:284:5 | LL | / impl From for () { LL | | @@ -474,7 +496,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:273:5 + --> $DIR/non_local_definitions.rs:293:5 | LL | / impl AsRef for () { LL | | @@ -488,7 +510,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:284:5 + --> $DIR/non_local_definitions.rs:304:5 | LL | / impl PartialEq for G { LL | | @@ -504,7 +526,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:301:5 + --> $DIR/non_local_definitions.rs:321:5 | LL | / impl PartialEq for &Dog { LL | | @@ -520,7 +542,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:308:5 + --> $DIR/non_local_definitions.rs:328:5 | LL | / impl PartialEq<()> for Dog { LL | | @@ -536,7 +558,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:315:5 + --> $DIR/non_local_definitions.rs:335:5 | LL | / impl PartialEq<()> for &Dog { LL | | @@ -552,7 +574,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:322:5 + --> $DIR/non_local_definitions.rs:342:5 | LL | / impl PartialEq for () { LL | | @@ -568,7 +590,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:344:5 + --> $DIR/non_local_definitions.rs:364:5 | LL | / impl From>> for () { LL | | @@ -584,7 +606,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:351:5 + --> $DIR/non_local_definitions.rs:371:5 | LL | / impl From<()> for Wrap { LL | | @@ -600,7 +622,7 @@ LL | | } = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:364:13 + --> $DIR/non_local_definitions.rs:384:13 | LL | impl MacroTrait for OutsideStruct {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -615,7 +637,7 @@ LL | m!(); = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) warning: non-local `impl` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:374:1 + --> $DIR/non_local_definitions.rs:394:1 | LL | non_local_macro::non_local_impl!(CargoUpdate); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -628,7 +650,7 @@ LL | non_local_macro::non_local_impl!(CargoUpdate); = note: this warning originates in the macro `non_local_macro::non_local_impl` (in Nightly builds, run with -Z macro-backtrace for more info) warning: non-local `macro_rules!` definition, they should be avoided as they go against expectation - --> $DIR/non_local_definitions.rs:377:1 + --> $DIR/non_local_definitions.rs:397:1 | LL | non_local_macro::non_local_macro_rules!(my_macro); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -640,5 +662,5 @@ LL | non_local_macro::non_local_macro_rules!(my_macro); = note: the macro `non_local_macro::non_local_macro_rules` may come from an old version of the `non_local_macro` crate, try updating your dependency with `cargo update -p non_local_macro` = note: this warning originates in the macro `non_local_macro::non_local_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) -warning: 50 warnings emitted +warning: 52 warnings emitted diff --git a/tests/ui/macros/macros-nonfatal-errors.rs b/tests/ui/macros/macros-nonfatal-errors.rs index 20e6d70500358..65cb899e0941b 100644 --- a/tests/ui/macros/macros-nonfatal-errors.rs +++ b/tests/ui/macros/macros-nonfatal-errors.rs @@ -1,4 +1,4 @@ -//@ normalize-stderr-test: "existed:.*\(" -> "existed: $$FILE_NOT_FOUND_MSG (" +//@ normalize-stderr-test: "`: .*\(" -> "`: $$FILE_NOT_FOUND_MSG (" // test that errors in a (selection) of macros don't kill compilation // immediately, so that we get more errors listed at a time. diff --git a/tests/ui/macros/macros-nonfatal-errors.stderr b/tests/ui/macros/macros-nonfatal-errors.stderr index ca373ea6cd9e4..d02d2399bbadb 100644 --- a/tests/ui/macros/macros-nonfatal-errors.stderr +++ b/tests/ui/macros/macros-nonfatal-errors.stderr @@ -200,7 +200,7 @@ error: argument must be a string literal LL | include_str!(invalid); | ^^^^^^^ -error: couldn't read $DIR/i'd be quite surprised if a file with this name existed: $FILE_NOT_FOUND_MSG (os error 2) +error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG (os error 2) --> $DIR/macros-nonfatal-errors.rs:113:5 | LL | include_str!("i'd be quite surprised if a file with this name existed"); @@ -214,7 +214,7 @@ error: argument must be a string literal LL | include_bytes!(invalid); | ^^^^^^^ -error: couldn't read $DIR/i'd be quite surprised if a file with this name existed: $FILE_NOT_FOUND_MSG (os error 2) +error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG (os error 2) --> $DIR/macros-nonfatal-errors.rs:115:5 | LL | include_bytes!("i'd be quite surprised if a file with this name existed"); diff --git a/tests/ui/parser/help-set-edition-ice-122130.rs b/tests/ui/parser/help-set-edition-ice-122130.rs new file mode 100644 index 0000000000000..bc5af04ecbc0c --- /dev/null +++ b/tests/ui/parser/help-set-edition-ice-122130.rs @@ -0,0 +1,5 @@ +enum will { + s#[c"owned_box"] + //~^ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `#` + //~|ERROR expected item, found `"owned_box"` +} diff --git a/tests/ui/parser/help-set-edition-ice-122130.stderr b/tests/ui/parser/help-set-edition-ice-122130.stderr new file mode 100644 index 0000000000000..fe4d212f2db65 --- /dev/null +++ b/tests/ui/parser/help-set-edition-ice-122130.stderr @@ -0,0 +1,21 @@ +error: expected one of `(`, `,`, `=`, `{`, or `}`, found `#` + --> $DIR/help-set-edition-ice-122130.rs:2:6 + | +LL | s#[c"owned_box"] + | ^ expected one of `(`, `,`, `=`, `{`, or `}` + | + = note: you may be trying to write a c-string literal + = note: c-string literals require Rust 2021 or later + = help: pass `--edition 2021` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error: expected item, found `"owned_box"` + --> $DIR/help-set-edition-ice-122130.rs:2:9 + | +LL | s#[c"owned_box"] + | ^^^^^^^^^^^ expected item + | + = note: for a full list of items that can appear in modules, see + +error: aborting due to 2 previous errors + diff --git a/tests/ui/suggestions/deref-path-method.stderr b/tests/ui/suggestions/deref-path-method.stderr index a2b68fa966fcb..b27d9aef06614 100644 --- a/tests/ui/suggestions/deref-path-method.stderr +++ b/tests/ui/suggestions/deref-path-method.stderr @@ -7,9 +7,9 @@ LL | Vec::contains(&vec, &0); note: if you're trying to build a new `Vec<_, _>` consider using one of the following associated functions: Vec::::new Vec::::with_capacity + Vec::::try_with_capacity Vec::::from_raw_parts - Vec::::new_in - and 2 others + and 4 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: the function `contains` is implemented on `[_]` | diff --git a/tests/ui/ufcs/bad-builder.stderr b/tests/ui/ufcs/bad-builder.stderr index e1c5e45b3ebb7..9cfeb7a5d09d6 100644 --- a/tests/ui/ufcs/bad-builder.stderr +++ b/tests/ui/ufcs/bad-builder.stderr @@ -7,9 +7,9 @@ LL | Vec::::mew() note: if you're trying to build a new `Vec` consider using one of the following associated functions: Vec::::new Vec::::with_capacity + Vec::::try_with_capacity Vec::::from_raw_parts - Vec::::new_in - and 2 others + and 4 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: there is an associated function `new` with a similar name |