diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 6850104f1cda6..c86d6366e0753 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -4096,6 +4096,7 @@ pub struct TestBinderConstraints { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct TestBinderBody { + pub span: Span, pub foralls: ThinVec, pub exists: ThinVec, pub constraints: Vec, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 14ce5ffc23314..abeafe2580171 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -2015,7 +2015,8 @@ impl<'hir> LoweringContext<'_, 'hir> { .iter() .map(|predicate| self.lower_where_predicate(predicate, &[], &mut dedup_map)), ); - hir::TestBinderBody { foralls, exists, constraints, predicates } + let span = self.lower_span(body.span); + hir::TestBinderBody { foralls, exists, constraints, predicates, span } } fn lower_test_binder_forall( diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index df18b029b02c0..677ad270258cc 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -40,14 +40,8 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t let llfn = if tcx.sess.target.arch == Arch::X86 && let Some(dllimport) = crate::common::get_dllimport(tcx, instance_def_id, sym) { - // When calling functions in generated import libraries, - // LLVM/ar_archive_writer needs the fully decorated name - // (as would have been in the declaring object file), but dlltool - // wants the name as exported (as would be in the def file) - // which may be missing decorations. - let using_dlltool = common::is_using_dlltool(&tcx.sess.target); let llfn = cx.declare_fn( - &common::i686_decorated_name(dllimport, using_dlltool, true, !using_dlltool), + &common::i686_decorated_name(dllimport, true, true), fn_abi, Some(instance), ); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 6513cc0542ea5..9f8348a13116c 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -3,7 +3,6 @@ use std::ops::Range; use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; use rustc_attr_ir::Linkage; use rustc_attr_ir::lang_items::LangItem; -use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -16,7 +15,6 @@ use rustc_middle::mono::MonoItem; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance}; use rustc_span::{Symbol, bug, span_bug}; -use rustc_target::spec::Arch; use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; @@ -268,11 +266,6 @@ fn check_and_apply_linkage<'ll, 'tcx>( llvm::set_initializer(g2, initializer); g2 - } else if cx.tcx.sess.target.arch == Arch::X86 - && common::is_using_dlltool(&cx.tcx.sess.target) - && let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym) - { - cx.declare_global(&common::i686_decorated_name(dllimport, true, true, false), llty) } else { // Generate an external declaration. // FIXME(nagisa): investigate whether it can be changed into define_global diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 18b5c001f9922..ee580cf5483d6 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -1,7 +1,5 @@ use std::borrow::Cow; -use std::env; use std::error::Error; -use std::ffi::OsString; use std::fs::{self, File}; use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; @@ -24,13 +22,9 @@ use tracing::trace; use super::metadata::{create_compressed_metadata_file, search_for_section}; use super::rmeta_link::{self, RmetaLinkCache}; use super::symbol_edit::{apply_edits, collect_internal_names}; -use crate::common; // Public for ArchiveBuilderBuilder::extract_bundled_libs pub use crate::diagnostics::ExtractBundledLibsError; -use crate::diagnostics::{ - ArchiveBuildFailure, DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, - ErrorWritingDEFFile, UnknownArchiveKind, -}; +use crate::diagnostics::{ArchiveBuildFailure, ErrorCreatingImportLibrary, UnknownArchiveKind}; /// An item to be included in an import library. /// This is a slimmed down version of `COFFShortExport` from `ar-archive-writer`. @@ -87,66 +81,57 @@ pub trait ArchiveBuilderBuilder { items: Vec, output_path: &Path, ) { - if common::is_using_dlltool(&sess.target) { - // The binutils linker used on -windows-gnu targets cannot read the import - // libraries generated by LLVM: in our attempts, the linker produced an .EXE - // that loaded but crashed with an AV upon calling one of the imported - // functions. Therefore, use binutils to create the import library instead, - // by writing a .DEF file to the temp dir and calling binutils's dlltool. - create_mingw_dll_import_lib(sess, lib_name, items, output_path); - } else { - trace!("creating import library"); - trace!(" dll_name {:#?}", lib_name); - trace!(" output_path {}", output_path.display()); - trace!( - " import names: {}", - items - .iter() - .map(|ImportLibraryItem { name, .. }| name.clone()) - .collect::>() - .join(", "), - ); - - // All import names are Rust identifiers and therefore cannot contain \0 characters. - // FIXME: when support for #[link_name] is implemented, ensure that the import names - // still don't contain any \0 characters. Also need to check that the names don't - // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved - // in definition files. - - let mut file = match fs::File::create_new(&output_path) { - Ok(file) => file, - Err(error) => sess - .dcx() - .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }), - }; + trace!("creating import library"); + trace!(" dll_name {:#?}", lib_name); + trace!(" output_path {}", output_path.display()); + trace!( + " import names: {}", + items + .iter() + .map(|ImportLibraryItem { name, .. }| name.clone()) + .collect::>() + .join(", "), + ); + + // All import names are Rust identifiers and therefore cannot contain \0 characters. + // FIXME: when support for #[link_name] is implemented, ensure that the import names + // still don't contain any \0 characters. Also need to check that the names don't + // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved + // in definition files. + + let mut file = match fs::File::create_new(&output_path) { + Ok(file) => file, + Err(error) => sess + .dcx() + .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }), + }; - let exports = - items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::>(); - let machine = match &sess.target.arch { - Arch::X86_64 => MachineTypes::AMD64, - Arch::X86 => MachineTypes::I386, - Arch::AArch64 => MachineTypes::ARM64, - Arch::Arm64EC => MachineTypes::ARM64EC, - Arch::Arm => MachineTypes::ARMNT, - cpu => panic!("unsupported cpu type {cpu}"), - }; + let exports = + items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::>(); + let machine = match &sess.target.arch { + Arch::X86_64 => MachineTypes::AMD64, + Arch::X86 => MachineTypes::I386, + Arch::AArch64 => MachineTypes::ARM64, + Arch::Arm64EC => MachineTypes::ARM64EC, + Arch::Arm => MachineTypes::ARMNT, + cpu => panic!("unsupported cpu type {cpu}"), + }; - if let Err(error) = ar_archive_writer::write_import_library( - &mut file, - lib_name, - &exports, - machine, - !sess.target.is_like_msvc, - // Enable compatibility with MSVC's `/WHOLEARCHIVE` flag. - // Without this flag a duplicate symbol error would be emitted - // when linking a rust staticlib using `/WHOLEARCHIVE`. - // See #129020 - true, - &[], - ) { - sess.dcx() - .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }); - } + if let Err(error) = ar_archive_writer::write_import_library( + &mut file, + lib_name, + &exports, + machine, + !sess.target.is_like_msvc, + // Enable compatibility with MSVC's `/WHOLEARCHIVE` flag. + // Without this flag a duplicate symbol error would be emitted + // when linking a rust staticlib using `/WHOLEARCHIVE`. + // See #129020 + true, + &[], + ) { + sess.dcx() + .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() }); } } @@ -187,129 +172,6 @@ pub trait ArchiveBuilderBuilder { } } -fn create_mingw_dll_import_lib( - sess: &Session, - lib_name: &str, - items: Vec, - output_path: &Path, -) { - let def_file_path = output_path.with_extension("def"); - - let def_file_content = format!( - "EXPORTS\n{}", - items - .into_iter() - .map(|ImportLibraryItem { name, ordinal, .. }| { - match ordinal { - Some(n) => format!("{name} @{n} NONAME"), - None => name, - } - }) - .collect::>() - .join("\n") - ); - - match std::fs::write(&def_file_path, def_file_content) { - Ok(_) => {} - Err(e) => { - sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e }); - } - }; - - // --no-leading-underscore: For the `import_name_type` feature to work, we need to be - // able to control the *exact* spelling of each of the symbols that are being imported: - // hence we don't want `dlltool` adding leading underscores automatically. - let dlltool = find_binutils_dlltool(sess); - // temp_prefix doesn't handle paths with spaces so - // use a relative path and set the current working directory - let cwd = output_path.parent().unwrap_or(output_path); - let temp_prefix = lib_name; - // dlltool target architecture args from: - // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69 - let (dlltool_target_arch, dlltool_target_bitness) = match &sess.target.arch { - Arch::X86_64 => ("i386:x86-64", "--64"), - Arch::X86 => ("i386", "--32"), - Arch::AArch64 => ("arm64", "--64"), - Arch::Arm => ("arm", "--32"), - arch => panic!("unsupported arch {arch}"), - }; - let mut dlltool_cmd = std::process::Command::new(&dlltool); - dlltool_cmd - .arg("-d") - .arg(def_file_path) - .arg("-D") - .arg(lib_name) - .arg("-l") - .arg(&output_path) - .arg("-m") - .arg(dlltool_target_arch) - .arg("-f") - .arg(dlltool_target_bitness) - .arg("--no-leading-underscore") - .arg("--temp-prefix") - .arg(temp_prefix) - .current_dir(cwd); - - match dlltool_cmd.output() { - Err(e) => { - sess.dcx().emit_fatal(ErrorCallingDllTool { - dlltool_path: dlltool.to_string_lossy(), - error: e, - }); - } - // dlltool returns '0' on failure, so check for error output instead. - Ok(output) if !output.stderr.is_empty() => { - sess.dcx().emit_fatal(DlltoolFailImportLibrary { - dlltool_path: dlltool.to_string_lossy(), - dlltool_args: dlltool_cmd - .get_args() - .map(|arg| arg.to_string_lossy()) - .collect::>() - .join(" "), - stdout: String::from_utf8_lossy(&output.stdout), - stderr: String::from_utf8_lossy(&output.stderr), - }) - } - _ => {} - } -} - -fn find_binutils_dlltool(sess: &Session) -> OsString { - assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); - if let Some(dlltool_path) = &sess.opts.cg.dlltool { - return dlltool_path.clone().into_os_string(); - } - - let tool_name: OsString = if sess.host.options.is_like_windows { - // If we're compiling on Windows, always use "dlltool.exe". - "dlltool.exe" - } else { - // On other platforms, use the architecture-specific name. - match sess.target.arch { - Arch::X86_64 => "x86_64-w64-mingw32-dlltool", - Arch::X86 => "i686-w64-mingw32-dlltool", - Arch::AArch64 => "aarch64-w64-mingw32-dlltool", - - // For non-standard architectures (e.g., aarch32) fallback to "dlltool". - _ => "dlltool", - } - } - .into(); - - // NOTE: it's not clear how useful it is to explicitly search PATH. - for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { - let full_path = dir.join(&tool_name); - if full_path.is_file() { - return full_path.into_os_string(); - } - } - - // The user didn't specify the location of the dlltool binary, and we weren't able - // to find the appropriate one on the PATH. Just return the name of the tool - // and let the invocation fail with a hopefully useful error message. - tool_name -} - pub enum AddArchiveKind<'a> { Rlib(&'a mut RmetaLinkCache, /*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool), Other, diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index 99436c4d2f84a..f5b3a8e113d6d 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -73,18 +73,16 @@ pub(super) fn create_raw_dylib_dll_import_libs<'a>( let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib")); - let using_dlltool = common::is_using_dlltool(&sess.target); - let items: Vec = raw_dylib_imports .iter() .map(|import: &DllImport| { if sess.target.arch == Arch::X86 { ImportLibraryItem { - name: common::i686_decorated_name(import, using_dlltool, false, false), + name: common::i686_decorated_name(import, false, false), ordinal: import.ordinal(), - symbol_name: import.is_missing_decorations().then(|| { - common::i686_decorated_name(import, using_dlltool, false, true) - }), + symbol_name: import + .is_missing_decorations() + .then(|| common::i686_decorated_name(import, false, true)), is_data: import.symbol_type != DllImportSymbolType::Function, } } else { diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 9338a6b3769ed..2b2ec59903391 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -7,7 +7,6 @@ use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Instance, ScalarInt, TyCtxt}; use rustc_span::{Span, bug, span_bug}; -use rustc_target::spec::{CfgAbi, Env, Os, Target}; use crate::traits::*; @@ -199,13 +198,8 @@ pub fn asm_const_ptr_clean<'tcx>(tcx: TyCtxt<'tcx>, scalar: Scalar) -> Scalar { } } -pub fn is_using_dlltool(target: &Target) -> bool { - target.os == Os::Windows && target.env == Env::Gnu && target.cfg_abi == CfgAbi::Unspecified -} - pub fn i686_decorated_name( dll_import: &DllImport, - mingw: bool, disable_name_mangling: bool, force_fully_decorated: bool, ) -> String { @@ -231,12 +225,10 @@ pub fn i686_decorated_name( let prefix = if add_prefix && dll_import.symbol_type == DllImportSymbolType::Function { match dll_import.calling_convention { DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None, - DllCallingConvention::Stdcall(_) => (!mingw - || dll_import.import_name_type == Some(PeImportNameType::Decorated)) - .then_some('_'), + DllCallingConvention::Stdcall(_) => Some('_'), DllCallingConvention::Fastcall(_) => Some('@'), } - } else if dll_import.symbol_type != DllImportSymbolType::Function && !mingw { + } else if dll_import.symbol_type != DllImportSymbolType::Function { // For static variables, prefix with '_' on MSVC. Some('_') } else { diff --git a/compiler/rustc_codegen_ssa/src/diagnostics.rs b/compiler/rustc_codegen_ssa/src/diagnostics.rs index 09faa71468bf7..f30b4850a22fc 100644 --- a/compiler/rustc_codegen_ssa/src/diagnostics.rs +++ b/compiler/rustc_codegen_ssa/src/diagnostics.rs @@ -1111,32 +1111,6 @@ pub struct FailedToGetLayout<'tcx> { pub err: LayoutError<'tcx>, } -#[derive(Diagnostic)] -#[diag( - "dlltool could not create import library with {$dlltool_path} {$dlltool_args}: -{$stdout} -{$stderr}" -)] -pub(crate) struct DlltoolFailImportLibrary<'a> { - pub dlltool_path: Cow<'a, str>, - pub dlltool_args: String, - pub stdout: Cow<'a, str>, - pub stderr: Cow<'a, str>, -} - -#[derive(Diagnostic)] -#[diag("error writing .DEF file: {$error}")] -pub(crate) struct ErrorWritingDEFFile { - pub error: std::io::Error, -} - -#[derive(Diagnostic)] -#[diag("error calling dlltool '{$dlltool_path}': {$error}")] -pub(crate) struct ErrorCallingDllTool<'a> { - pub dlltool_path: Cow<'a, str>, - pub error: std::io::Error, -} - #[derive(Diagnostic)] #[diag("failed to create remark directory: {$error}")] pub(crate) struct ErrorCreatingRemarkDir { diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3801fb2f5fa7c..17de5f003b27a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -770,7 +770,7 @@ declare_features! ( (incomplete, unsafe_fields, "1.85.0", Some(132922)), /// Allows const generic parameters to be defined with types that /// are not `Sized`, e.g. `fn foo() {`. - (incomplete, unsized_const_params, "1.82.0", Some(95174)), + (incomplete, unsized_const_params, "1.82.0", Some(128028)), /// Allows unsized fn parameters. (internal, unsized_fn_params, "1.49.0", Some(48055)), /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ff7f08dda5510..4b27781086b02 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4530,6 +4530,7 @@ impl FnHeader { #[derive(Debug, Clone, Copy, StableHash)] pub struct TestBinderBody<'hir> { + pub span: Span, pub foralls: &'hir [TestBinderForall<'hir>], pub exists: &'hir [TestBinderExists<'hir>], /// Constraints to be inserted directly into constraint storage to be proven diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 2f934d7ef728d..0fa40d0409854 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1600,7 +1600,7 @@ pub fn walk_test_binder_body<'v, V: Visitor<'v>>( visitor: &mut V, body: &'v TestBinderBody<'v>, ) -> V::Result { - let TestBinderBody { foralls, exists, constraints, predicates } = body; + let TestBinderBody { span: _, foralls, exists, constraints, predicates } = body; walk_list!(visitor, visit_test_binder_forall, *foralls); walk_list!(visitor, visit_test_binder_exists, *exists); try_visit!(visitor.visit_test_binder_constraint(&constraints)); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 2074213c167b2..1eaa8545c1b6d 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2380,7 +2380,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self))] pub(super) fn check_test_binder_body(&self, body: TestBinderBody<'tcx>) { - let TestBinderBody { foralls, exists, constraints, predicates } = body; + let TestBinderBody { span, foralls, exists, constraints, predicates } = body; if !predicates.is_empty() { for (predicate, span) in predicates { let cause = traits::ObligationCause::misc(span, self.body_def_id); @@ -2400,6 +2400,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { Ok(()) => constraints, Err(_guar) => ty::region_constraint::RegionConstraint::new_true(), }; + if constraints.is_false() { + self.tcx().dcx().span_err(span, "top level false constraints are not supported"); + return; + } self.infcx.register_solver_region_constraint(constraints); @@ -2479,6 +2483,13 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { u, ) .with_spans(forall.span); + if constraint.is_false() { + self.tcx().dcx().span_err(forall.span, "forall evaluated to false"); + self.infcx.overwrite_solver_region_constraint( + ty::region_constraint::RegionConstraint::new_true(), + ); + return; + } if let Some(assert_on_exit) = &forall.assert_on_exit { self.check_test_binder_region_constraints(forall.span, assert_on_exit, &constraint); } @@ -2798,6 +2809,7 @@ struct RedundantLifetimeArgsLint<'tcx> { #[derive(Clone, Debug, TypeFoldable, TypeVisitable)] pub(crate) struct TestBinderBody<'tcx> { + pub span: Span, pub foralls: Vec>, pub exists: Vec>, /// Constraints to be inserted directly into constraint storage to be proven diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 1965ade38204e..7f181ef2e7e04 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -328,7 +328,7 @@ impl<'tcx> ItemCtxt<'tcx> { &self, item: &hir::TestBinderBody<'tcx>, ) -> TestBinderBody<'tcx> { - let hir::TestBinderBody { foralls, exists, constraints, predicates } = item; + let hir::TestBinderBody { span, foralls, exists, constraints, predicates } = item; let foralls = foralls.iter().map(|forall| self.lower_test_binder_forall(forall)).collect(); let exists = exists.iter().map(|exists| self.lower_test_binder_exists(exists)).collect(); let constraints = self.lower_test_binder_constraint(&constraints); @@ -337,7 +337,7 @@ impl<'tcx> ItemCtxt<'tcx> { clauses_of::where_predicate_clauses(self, predicate, &mut clauses); } let predicates = clauses.into_iter().map(|(c, span)| (c.kind(), span)).collect(); - TestBinderBody { foralls, exists, constraints, predicates } + TestBinderBody { span: *span, foralls, exists, constraints, predicates } } #[instrument(level = "debug", skip(self), ret)] diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 34ac92faef88c..f61a8da491c77 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -607,7 +607,6 @@ fn test_codegen_options_tracking_hash() { // tidy-alphabetical-start untracked!(codegen_units, Some(42)); untracked!(default_linker_libraries, true); - untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe"))); untracked!(extra_filename, String::from("extra-filename")); untracked!(incremental, Some(String::from("abc"))); // `link_arg` is omitted because it just forwards to `link_args`. diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 06f354fef2413..3d2a98ca3bc23 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -9,7 +9,7 @@ use std::{env, thread}; use rand::{RngCore, rng}; use rustc_ast as ast; use rustc_attr_parsing::ShouldEmit; -use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder}; +use rustc_codegen_ssa::back::archive::ArArchiveBuilderBuilder; use rustc_codegen_ssa::back::link::link_binary; use rustc_codegen_ssa::target_features::internal_target_features; use rustc_codegen_ssa::traits::CodegenBackend; @@ -476,7 +476,7 @@ impl CodegenBackend for DummyCodegenBackend { link_binary( sess, - &DummyArchiveBuilderBuilder, + &ArArchiveBuilderBuilder, compiled_modules, crate_info, metadata, @@ -486,28 +486,6 @@ impl CodegenBackend for DummyCodegenBackend { } } -struct DummyArchiveBuilderBuilder; - -impl ArchiveBuilderBuilder for DummyArchiveBuilderBuilder { - fn new_archive_builder<'a>( - &self, - sess: &'a Session, - ) -> Box { - ArArchiveBuilderBuilder.new_archive_builder(sess) - } - - fn create_dll_import_lib( - &self, - sess: &Session, - _lib_name: &str, - _items: Vec, - output_path: &Path, - ) { - // Build an empty static library to avoid calling an external dlltool on mingw - ArArchiveBuilderBuilder.new_archive_builder(sess).build(output_path, None); - } -} - // This is used for rustdoc, but it uses similar machinery to codegen backend // loading, so we leave the code here. It is potentially useful for other tools // that want to invoke the rustc binary while linking to rustc as well. diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index 8b575033d0df6..5167737ac001f 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -1212,10 +1212,6 @@ pub(crate) struct QueryUntracked { pub method: Symbol, } -#[derive(Diagnostic)] -#[diag("use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`")] -pub(crate) struct SpanUseEqCtxtDiag; - #[derive(Diagnostic)] #[diag("using `{$fn_name}` on a string literal")] #[help("consider adding the symbol to `compiler/rustc_span/src/symbol.rs`")] diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index bce726311e51f..03622e253c90a 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -20,8 +20,8 @@ use crate::diagnostics::{ AttributeKindInFindAttr, BadOptAccessDiag, DefaultHashTypesDiag, ImplicitSysrootCrateImportDiag, LintPassByHand, MissingGenericTypeVisitableDeriveDiag, NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, - RustcMustMatchExhaustivelyNotExhaustive, SpanUseEqCtxtDiag, SymbolInternStringLiteralDiag, - TyQualified, TykindDiag, TykindKind, TypeIrDirectUse, TypeIrInherentUsage, TypeIrTraitUsage, + RustcMustMatchExhaustivelyNotExhaustive, SymbolInternStringLiteralDiag, TyQualified, + TykindDiag, TykindKind, TypeIrDirectUse, TypeIrInherentUsage, TypeIrTraitUsage, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -570,41 +570,6 @@ impl LateLintPass<'_> for BadOptAccess { } } -declare_tool_lint! { - pub rustc::SPAN_USE_EQ_CTXT, - Allow, - "forbid uses of `==` with `Span::ctxt`, suggest `Span::eq_ctxt` instead", - report_in_external_macro: true -} - -declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]); - -impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { - if let hir::ExprKind::Binary( - hir::BinOp { node: hir::BinOpKind::Eq | hir::BinOpKind::Ne, .. }, - lhs, - rhs, - ) = expr.kind - { - if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) { - cx.emit_span_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag); - } - } - } -} - -fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - match &expr.kind { - hir::ExprKind::MethodCall(..) => cx - .typeck_results() - .type_dependent_def_id(expr.hir_id) - .is_some_and(|call_did| cx.tcx.is_diagnostic_item(sym::SpanCtxt, call_did)), - - _ => false, - } -} - declare_tool_lint! { /// The `symbol_intern_string_literal` lint detects `Symbol::intern` or `Ident::from_str_and_span` being called on a string literal pub rustc::SYMBOL_INTERN_STRING_LITERAL, diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 88d99449ba879..94f6183740954 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -301,7 +301,6 @@ late_lint_methods!( MissingGenericTypeVisitableDerive: MissingGenericTypeVisitableDerive, QueryStability: QueryStability, RustcMustMatchExhaustively: RustcMustMatchExhaustively, - SpanUseEqCtxt: SpanUseEqCtxt, SymbolInternStringLiteral: SymbolInternStringLiteral, TyTyKind: TyTyKind, TypeIr: TypeIr, @@ -759,8 +758,6 @@ fn register_internals(store: &mut LintStore) { LintId::of(BAD_OPT_ACCESS), // Late pass: DisallowedPassByRef LintId::of(DISALLOWED_PASS_BY_REF), - // Late pass: SpanUseEqCtxt - LintId::of(SPAN_USE_EQ_CTXT), // Late pass: SymbolInternStringLiteral // Note: this one is not included in rustc::internal because rustc_driver crates // outside the compiler can't/shouldn't add preinterned symbols. For rustc itself, diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index 54d91eca01327..a942d6b060dab 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -1712,6 +1712,15 @@ pub(crate) struct GenericParamsWithoutAngleBrackets { pub sugg: GenericParamsWithoutAngleBracketsSugg, } +#[derive(Diagnostic)] +#[diag("use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments")] +pub(crate) struct MissingTurbofishForLifetime { + #[primary_span] + pub span: Span, + #[suggestion("add `::`", style = "verbose", code = "::", applicability = "machine-applicable")] + pub suggest_turbofish: Span, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( "surround the type parameters with angle brackets", diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 705aac9cb17a6..d734a70399b11 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1144,6 +1144,13 @@ impl<'a> Parser<'a> { } } + pub(super) fn recover_missing_turbofish_for_lifetimes(&self, lt_span: Span) { + self.dcx().emit_err(crate::diagnostics::MissingTurbofishForLifetime { + span: lt_span, + suggest_turbofish: lt_span.shrink_to_lo(), + }); + } + /// When writing a turbofish with multiple type parameters missing the leading `::`, we will /// encounter a parse error when encountering the first `,`. pub(super) fn check_mistyped_turbofish_with_multiple_type_params( diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d04d4c9818c5b..5ad0aae9a916a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -410,12 +410,6 @@ impl<'a> Parser<'a> { pre_span, )); } - token::Ident(..) - if this.token.is_keyword(kw::Move) - && this.look_ahead(1, |t| *t == token::OpenParen) => - { - make_it!(this, attrs, |this, _| this.parse_expr_move(lo)) - } token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => { make_it!(this, attrs, |this, _| this.recover_not_expr(lo)) } @@ -447,16 +441,6 @@ impl<'a> Parser<'a> { self.parse_expr_unary(lo, UnOp::Not) } - fn parse_expr_move(&mut self, move_kw: Span) -> PResult<'a, (Span, ExprKind)> { - self.bump(); - self.psess.gated_spans.gate(sym::move_expr, move_kw); - self.expect(exp!(OpenParen))?; - let expr = self.parse_expr()?; - self.expect(exp!(CloseParen))?; - let span = move_kw.to(self.prev_token.span); - Ok((span, ExprKind::Move(expr, move_kw))) - } - fn is_mistaken_not_ident_negation(&self) -> bool { let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind { // These tokens can start an expression after `!`, but @@ -1335,6 +1319,10 @@ impl<'a> Parser<'a> { this.parse_expr_builtin() } else if this.check_path() { this.parse_expr_path_start() + } else if this.token.is_keyword(kw::Move) + && this.look_ahead(1, |t| *t == token::OpenParen) + { + this.parse_expr_move() } else if this.check_keyword(exp!(Move)) || this.check_keyword(exp!(Use)) || this.check_keyword(exp!(Static)) @@ -2216,6 +2204,17 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None))) } + fn parse_expr_move(&mut self) -> PResult<'a, Box> { + let move_kw = self.token.span; + self.expect_keyword(exp!(Move))?; + self.expect(exp!(OpenParen))?; + let expr = self.parse_expr()?; + self.expect(exp!(CloseParen))?; + let span = move_kw.to(self.prev_token.span); + self.psess.gated_spans.gate(sym::move_expr, span); + Ok(self.mk_expr(span, ExprKind::Move(expr, move_kw))) + } + /// Parses a closure expression (e.g., `move |args| expr`). fn parse_expr_closure(&mut self) -> PResult<'a, Box> { let lo = self.token.span; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index ee0f317f637d7..2011f8646346e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2731,6 +2731,7 @@ impl<'a> Parser<'a> { let mut exists = ThinVec::new(); let mut constraints = Vec::new(); let mut predicates = Vec::new(); + let mut span = self.token.span; self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |this| { match this.token.ident() { Some((Ident { name: sym::predicates, .. }, IdentKind::Normal)) => { @@ -2749,7 +2750,8 @@ impl<'a> Parser<'a> { } Ok(()) })?; - Ok(TestBinderBody { foralls, exists, constraints, predicates }) + span = span.to(self.prev_token.span); + Ok(TestBinderBody { foralls, exists, constraints, predicates, span }) } pub fn parse_test_binder_forall(&mut self) -> PResult<'a, TestBinderForall> { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 59d8bf932d430..8dfe95a849449 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -303,9 +303,20 @@ impl<'a> Parser<'a> { is_args_start(&this.token) }; + // A lifetime followed by `>` or `,` can never start a valid expression, so `<` here + // must be the start of generic arguments with a missing `::`. + let is_missing_turbofish_for_lifetime = |this: &mut Self| { + this.token == token::Lt + && this.may_recover() + && style == PathStyle::Expr + && this.look_ahead(1, |t| t.is_lifetime()) + && this.look_ahead(2, |t| matches!(t.kind, token::Gt | token::Comma)) + }; + Ok( if style == PathStyle::Type && check_args_start(self) || style != PathStyle::Mod && self.check_path_sep_and_look_ahead(is_args_start) + || is_missing_turbofish_for_lifetime(self) { // We use `style == PathStyle::Expr` to check if this is in a recursion or not. If // it isn't, then we reset the unmatched angle bracket count as we're about to start @@ -314,12 +325,18 @@ impl<'a> Parser<'a> { self.unmatched_angle_bracket_count = 0; } + let missing_turbofish = + style == PathStyle::Expr && !self.check_path_sep_and_look_ahead(is_args_start); + // Generic arguments are found - `<`, `(`, `::<` or `::(`. // First, eat `::` if it exists. let _ = self.eat_path_sep(); let lo = self.token.span; let args = if self.eat_lt() { + if missing_turbofish { + self.recover_missing_turbofish_for_lifetimes(lo); + } // `<'a, T, A = U>` let args = self.parse_angle_args_with_leading_angle_bracket_recovery( style, diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index f1400c83e4295..18a02666cfc4a 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -206,21 +206,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .iter() .map(|(import, import_error)| { if let Some(ModuleOrUniformRoot::Module(module_data)) = import.imported_module.get() - && let ModuleKind::Def(DefKind::Mod, def_id, _, name) = module_data.kind + && let ModuleKind::Def(DefKind::Mod, def_id, ..) = module_data.kind { - let Some(directive) = self.on_unknown_data(def_id) else { + let Some((this, directive)) = self.on_unknown_data(def_id) else { return CustomDiagnostic::default(); }; - let this = if let Some(name) = name { - name.to_string() - } else if let Some(crate_name) = &self.tcx.sess.opts.crate_name { - crate_name.to_string() - } else { - "".to_string() - }; let unresolved = import_error.segment.map(|s| s.name).unwrap_or(kw::Underscore); - let args = FormatArgs { this, unresolved: unresolved.to_string(), .. }; + let args = FormatArgs { + this: this.to_string(), + unresolved: unresolved.to_string(), + .. + }; directive.eval(None, &args) } else { @@ -3867,12 +3864,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - /// Gets the `#[diagnostic::on_unknown]` attribute data associated with this `DefId`. - pub(crate) fn on_unknown_data(&self, def_id: DefId) -> Option<&Directive> { - match def_id.as_local() { + /// Gets the name and `#[diagnostic::on_unknown]` attribute data associated with this `DefId`. + pub(crate) fn on_unknown_data(&self, def_id: DefId) -> Option<(Symbol, &Directive)> { + let directive = match def_id.as_local() { Some(local) => Some(self.on_unknown_data.get(&local)?.directive.as_ref()), - None => find_attr!(self.tcx, def_id, OnUnknown{ directive } => directive)?.as_deref(), - } + None => find_attr!(self.tcx, def_id, OnUnknown { directive } => directive)?.as_deref(), + }?; + + let name = + self.expect_module(def_id).name().unwrap_or_else(|| self.tcx.crate_name(def_id.krate)); + + Some((name, directive)) } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 821c16ceee3e5..83e5fbef9bcb9 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -558,9 +558,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }; let mut notes = Vec::new(); if let Some(module_def_id) = module - && let Some(directive) = self.r.on_unknown_data(module_def_id) + && let Some((this, directive)) = self.r.on_unknown_data(module_def_id) { - let args = FormatArgs { unresolved: item_ident.to_string(), this: mod_str, .. }; + let args = + FormatArgs { unresolved: item_ident.to_string(), this: this.to_string(), .. }; let CustomDiagnostic { message, label, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index a29309c71e879..d9d107bd44207 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2244,8 +2244,9 @@ options! { line-tables-only, limited, or full; default: 0)"), default_linker_libraries: bool = (false, parse_bool, [UNTRACKED], "allow the linker to link its default libraries (default: no)"), - dlltool: Option = (None, parse_opt_pathbuf, [UNTRACKED], - "import library generation tool (ignored except when targeting windows-gnu)"), + dlltool: () = ((), parse_ignore, [UNTRACKED], + "this option has been removed", + removed: Warn), #[rustc_lint_opt_deny_field_access("use `Session::dwarf_version` instead of this field")] dwarf_version: Option = (None, parse_opt_number, [TRACKED], "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index f4cba4384d589..b036e2de05b02 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -479,7 +479,6 @@ impl Span { /// This function is used as a fast path when decoding the full `SpanData` is not necessary. /// It's a cut-down version of `data_untracked` and doesn't require taking the interner lock. - #[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")] #[inline] pub fn ctxt(self) -> SyntaxContext { let ctxt32 = match_span_kind! { @@ -492,12 +491,8 @@ impl Span { SyntaxContext::from_u32(ctxt32) } - // Allow `span_use_eq_ctxt` because this is `eq_ctxt`! - // - // FIXME(nnethercote): context equality used to be much more complex, but the span - // representation changed and now simple equality is fine. This method and the - // `span_use_eq_ctxt` lint and the `SpanCtxt` diagnostic item can be removed. - #[allow(rustc::span_use_eq_ctxt)] + // With the pre-#163857 span representation this method was more complex and required. Now it's + // trivial but it hasn't been removed simply to avoid churn. #[inline] pub fn eq_ctxt(self, other: Span) -> bool { self.ctxt() == other.ctxt() diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2fab2cbbe7a20..59e167a9f658f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -330,7 +330,6 @@ symbols! { SliceIndex, Some, Source, - SpanCtxt, StdinLock, Str, String, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index fb5472e0e8e28..abf1bf23ad2b9 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -646,7 +646,7 @@ bitflags::bitflags! { const LIBC = 1 << 1; /// libgcc/libunwind (e.g. on `windows-gnu`, `fuchsia`, `fortanix`, `gnullvm` targets) const UNWIND = 1 << 2; - /// Linker, dlltool, and their necessary libraries (e.g. on `windows-gnu` and for `rust-lld`) + /// Linker and its necessary libraries (e.g. for `rust-lld`) const LINKER = 1 << 3; /// Sanitizer runtime libraries const SANITIZERS = 1 << 4; diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 22d6b6027e00a..1c5718473a06b 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -53,20 +53,26 @@ //! //! # Memory layout //! -//! For non-zero-sized values, a [`Box`] will use the [`Global`] allocator for its allocation. It is -//! valid to convert both ways between a [`Box`] and a raw pointer allocated with the [`Global`] -//! allocator, given that the [`Layout`] used with the allocator is correct for the type and the raw -//! pointer points to a valid value of the right type. More precisely, a `value: *mut T` that has -//! been allocated with the [`Global`] allocator with `Layout::for_value(&*value)` may be converted -//! into a box using [`Box::::from_raw(value)`]. Conversely, the memory backing a `value: *mut T` -//! obtained from [`Box::::into_raw`] may be deallocated using the [`Global`] allocator with -//! [`Layout::for_value(&*value)`]. +//! For non-zero-sized values, a [`Box`] will use its allocator of type `A` for its allocation. +//! It is valid to convert both ways between a `Box` and a raw pointer allocated with `A`, so long as: +//! - the size of the pointee, as reported by [`size_of_val`], is non-zero, +//! - the [`Layout::for_value`] of the pointee [*fits*](Allocator#memory-fitting) the memory block, and +//! - the pointee is a valid instance of `T` (for the given metadata of the pointer). //! -//! For zero-sized values, the `Box` pointer has to be non-null and sufficiently aligned. The -//! recommended way to build a Box to a ZST if `Box::new` cannot be used is to use -//! [`ptr::NonNull::dangling`]. +//! For zero-sized values, a [`Box`] may use any sufficiently aligned non-null pointer. +//! While it is sound to convert a zero-sized allocation from `A` into a `Box`, as +//! long as it is backed by a valid value of `T`, doing so may lead to memory leaks, as +//! the destructor of [`Box`] will not call [`deallocate`](Allocator::deallocate) +//! on zero-sized values. It is recommended to use [`ptr::dangling`] instead. //! -//! On top of these basic layout requirements, a `Box` must point to a valid value of `T`. +//! Likewise, manually deallocating the raw pointer acquired from a `Box` is only sound +//! if the size of the pointee is non-zero, as zero-sized values may be represented by +//! a pointer that is not [*currently allocated*] by that allocator. It is recommended to +//! pair [`Box::into_raw_with_allocator`] with [`Box::from_raw_in`], rather than +//! doing this. +//! +//! Note that even the [`Global`] allocator might not use dangling pointers for +//! zero-sized allocations, even though *most* implementations do this. //! //! So long as `T: Sized`, a `Box` is guaranteed to be represented //! as a single pointer and is also ABI-compatible with C pointers @@ -172,6 +178,7 @@ //! and it's best to avoid relying on this edition-dependent behavior if you wish to preserve //! compatibility with future versions of the compiler. //! +//! [*currently allocated*]: Allocator#currently-allocated-memory //! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198 //! [ucg#326]: https://github.com/rust-lang/unsafe-code-guidelines/issues/326 //! [dereferencing]: core::ops::Deref @@ -343,12 +350,15 @@ impl Box { /// Allocates memory on the heap and then places `x` into it. /// /// This doesn't actually allocate if `T` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// /// ``` /// let five = Box::new(5); /// ``` + /// + /// [memory layout]: self#memory-layout #[cfg(not(no_global_oom_handling))] #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] @@ -435,6 +445,7 @@ impl Box { /// returning an error if the allocation fails /// /// This doesn't actually allocate if `T` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -444,6 +455,8 @@ impl Box { /// let five = Box::try_new(5)?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` + /// + /// [memory layout]: self#memory-layout #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn try_new(x: T) -> Result { @@ -502,6 +515,7 @@ impl Box { /// Allocates memory in the given allocator then places `x` into it. /// /// This doesn't actually allocate if `T` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -510,6 +524,8 @@ impl Box { /// /// let five = Box::new_in(5, System); /// ``` + /// + /// [memory layout]: self#memory-layout #[cfg(not(no_global_oom_handling))] #[stable(feature = "allocator_api", since = "1.100.0")] #[must_use] @@ -525,6 +541,7 @@ impl Box { /// returning an error if the allocation fails /// /// This doesn't actually allocate if `T` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -536,6 +553,8 @@ impl Box { /// let five = Box::try_new_in(5, System)?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` + /// + /// [memory layout]: self#memory-layout #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] pub fn try_new_in(x: T, alloc: A) -> Result { @@ -842,6 +861,7 @@ impl Box { /// Allocates memory on the heap then clones `src` into it. /// /// This doesn't actually allocate if `src` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -850,6 +870,8 @@ impl Box { /// /// let hello: Box = Box::clone_from_ref("hello"); /// ``` + /// + /// [memory layout]: self#memory-layout #[cfg(not(no_global_oom_handling))] #[unstable(feature = "clone_from_ref", issue = "149075")] #[must_use] @@ -861,6 +883,7 @@ impl Box { /// Allocates memory on the heap then clones `src` into it, returning an error if allocation fails. /// /// This doesn't actually allocate if `src` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -870,6 +893,8 @@ impl Box { /// let hello: Box = Box::try_clone_from_ref("hello")?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` + /// + /// [memory layout]: self#memory-layout #[unstable(feature = "clone_from_ref", issue = "149075")] //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] @@ -882,6 +907,7 @@ impl Box { /// Allocates memory in the given allocator then clones `src` into it. /// /// This doesn't actually allocate if `src` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -892,6 +918,8 @@ impl Box { /// /// let hello: Box = Box::clone_from_ref_in("hello", System); /// ``` + /// + /// [memory layout]: self#memory-layout #[cfg(not(no_global_oom_handling))] #[unstable(feature = "clone_from_ref", issue = "149075")] //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] @@ -908,6 +936,7 @@ impl Box { /// Allocates memory in the given allocator then clones `src` into it, returning an error if allocation fails. /// /// This doesn't actually allocate if `src` is zero-sized. + /// See the [memory layout] section for more details. /// /// # Examples /// @@ -919,6 +948,8 @@ impl Box { /// let hello: Box = Box::try_clone_from_ref_in("hello", System)?; /// # Ok::<(), std::alloc::AllocError>(()) /// ``` + /// + /// [memory layout]: self#memory-layout #[unstable(feature = "clone_from_ref", issue = "149075")] //#[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] #[inline] @@ -1368,9 +1399,16 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The raw pointer must point to a block of memory allocated by the global allocator. + /// The exact safety requirements are described in the [memory layout] section, together + /// with recommendations about how to handle the special case of zero-sized allocations + /// correctly. + /// + /// In particular, if the size of the pointee, as reported by [`size_of_val`], is + /// nonzero, then the raw pointer must point to a block of memory allocated by + /// [`Global`]. + /// If the size of the pointee is zero, then the raw pointer can be any + /// sufficiently aligned non-null pointer, e.g. [`ptr::dangling`]. /// - /// The safety conditions are described in the [memory layout] section. /// Note that the [considerations for unsafe code] apply to all `Box` values. /// /// # Examples @@ -1420,9 +1458,16 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same `NonNull` pointer. /// - /// The non-null pointer must point to a block of memory allocated by the global allocator. + /// The exact safety requirements are described in the [memory layout] section, together + /// with recommendations about how to handle the special case of zero-sized allocations + /// correctly. + /// + /// In particular, if the size of the pointee, as reported by [`size_of_val`], is + /// nonzero, then the raw pointer must point to a block of memory allocated by + /// [`Global`]. + /// If the size of the pointee is zero, then the raw pointer can be any + /// sufficiently aligned pointer, e.g. [`NonNull::dangling`]. /// - /// The safety conditions are described in the [memory layout] section. /// Note that the [considerations for unsafe code] apply to all `Box` values. /// /// # Examples @@ -1471,6 +1516,11 @@ impl Box { /// [`Box::from_raw`] function, allowing the `Box` destructor to perform /// the cleanup. /// + /// While manually deallocating the pointer through [`deallocate`] + /// is possible, you must only do this if the pointee's size, as reported by + /// [`size_of_val`], is non-zero (the `Box` may be a dangling pointer + /// otherwise). See the [memory layout] section for more details. + /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_raw(b)` instead of `b.into_raw()`. This /// is so that there is no conflict with a method on the inner type. @@ -1506,6 +1556,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + /// [`deallocate`]: Allocator::deallocate #[must_use = "losing the pointer will leak memory"] #[stable(feature = "box_raw", since = "1.4.0")] #[inline] @@ -1533,6 +1584,11 @@ impl Box { /// [`Box::from_non_null`] function, allowing the `Box` destructor to /// perform the cleanup. /// + /// While manually deallocating the pointer through [`deallocate`] + /// is possible, you must only do this if the pointee's size, as reported by + /// [`size_of_val`], is non-zero (the `Box` may be a dangling pointer + /// otherwise). See the [memory layout] section for more details. + /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_non_null(b)` instead of `b.into_non_null()`. /// This is so that there is no conflict with a method on the inner type. @@ -1567,6 +1623,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + /// [`deallocate`]: Allocator::deallocate #[must_use = "losing the pointer will leak memory"] #[stable(feature = "box_vec_non_null", since = "1.99.0")] #[inline] @@ -1585,9 +1642,8 @@ impl Box { /// /// After calling this function, the raw pointer is owned by the /// resulting `Box`. Specifically, the `Box` destructor will call - /// the destructor of `T` and free the allocated memory. For this - /// to be safe, the memory must have been allocated in accordance - /// with the [memory layout] used by `Box` . + /// the destructor of `T` and free the allocated memory, except + /// in the cases described in the [memory layout] section. /// /// # Safety /// @@ -1595,9 +1651,16 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The raw pointer must point to a block of memory allocated by `alloc`. + /// The exact safety requirements are described in the [memory layout] section, together + /// with recommendations about how to handle the special case of zero-sized allocations + /// correctly. + /// + /// In particular, if the size of the pointee, as reported by [`size_of_val`], is + /// nonzero, then the raw pointer must point to a block of memory allocated by + /// `alloc`. + /// If the size of the pointee is zero, then the raw pointer can be any + /// sufficiently aligned non-null pointer, e.g. [`ptr::dangling`]. /// - /// The safety conditions are described in the [memory layout] section. /// Note that the [considerations for unsafe code] apply to all `Box` values. /// /// # Examples @@ -1641,9 +1704,8 @@ impl Box { /// /// After calling this function, the `NonNull` pointer is owned by /// the resulting `Box`. Specifically, the `Box` destructor will call - /// the destructor of `T` and free the allocated memory. For this - /// to be safe, the memory must have been allocated in accordance - /// with the [memory layout] used by `Box` . + /// the destructor of `T` and free the allocated memory, except + /// in the cases described in the [memory layout] section. /// /// # Safety /// @@ -1651,9 +1713,16 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The non-null pointer must point to a block of memory allocated by `alloc`. + /// The exact safety requirements are described in the [memory layout] section, together + /// with recommendations about how to handle the special case of zero-sized allocations + /// correctly. + /// + /// In particular, if the size of the pointee, as reported by [`size_of_val`], is + /// nonzero, then the raw pointer must point to a block of memory allocated by + /// `alloc`. + /// If the size of the pointee is zero, then the raw pointer can be any + /// sufficiently aligned pointer, e.g. [`NonNull::dangling`]. /// - /// The safety conditions are described in the [memory layout] section. /// Note that the [considerations for unsafe code] apply to all `Box` values. /// /// # Examples @@ -1702,6 +1771,11 @@ impl Box { /// [`Box::from_raw_in`] function, allowing the `Box` destructor to perform /// the cleanup. /// + /// While manually deallocating the pointer through [`deallocate`] + /// is possible, you must only do this if the pointee's size, as reported by + /// [`size_of_val`], is non-zero (the `Box` may be a dangling pointer + /// otherwise). See the [memory layout] section for more details. + /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_raw_with_allocator(b)` instead of `b.into_raw_with_allocator()`. This /// is so that there is no conflict with a method on the inner type. @@ -1732,6 +1806,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + /// [`deallocate`]: Allocator::deallocate #[must_use = "losing the pointer will leak memory"] #[stable(feature = "allocator_api", since = "1.100.0")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] @@ -1761,6 +1836,11 @@ impl Box { /// [`Box::from_non_null_in`] function, allowing the `Box` destructor to /// perform the cleanup. /// + /// While manually deallocating the pointer through [`deallocate`] + /// is possible, you must only do this if the pointee's size, as reported by + /// [`size_of_val`], is non-zero (the `Box` may be a dangling pointer + /// otherwise). See the [memory layout] section for more details. + /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_non_null_with_allocator(b)` instead of /// `b.into_non_null_with_allocator()`. This is so that there is no @@ -1790,6 +1870,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + /// [`deallocate`]: Allocator::deallocate #[must_use = "losing the pointer will leak memory"] #[stable(feature = "allocator_api", since = "1.100.0")] #[inline] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 03c07b15712bb..1d864b061a825 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -940,6 +940,10 @@ impl String { /// into a `String` with the [`from_raw_parts`] function, allowing /// the destructor to perform the cleanup. /// + /// If `capacity == 0`, then `pointer` may be dangling. Otherwise, + /// it will always be valid for reads and writes over `capacity` + /// bytes, and point to the start of the string. + /// /// [`from_raw_parts`]: String::from_raw_parts /// /// # Examples diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 1a151e4eebb33..d51eb95e9eeec 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -51,25 +51,50 @@ //! //! # Memory layout //! -//! When the type is non-zero-sized and the capacity is nonzero, [`Vec`] uses the [`Global`] -//! allocator for its allocation. It is valid to convert both ways between such a [`Vec`] and a raw -//! pointer allocated with the [`Global`] allocator, provided that the [`Layout`] used with the -//! allocator is correct for a sequence of `capacity` elements of the type, and the first `len` -//! values pointed to by the raw pointer are valid. More precisely, a `ptr: *mut T` that has been -//! allocated with the [`Global`] allocator with [`Layout::array::(capacity)`][Layout::array] may -//! be converted into a vec using -//! [`Vec::::from_raw_parts(ptr, len, capacity)`](Vec::from_raw_parts). Conversely, the memory -//! backing a `value: *mut T` obtained from [`Vec::::as_mut_ptr`] may be deallocated using the -//! [`Global`] allocator with the same layout. +//! A [`Vec`] conceptually consists of a raw pointer `ptr` to the start of the allocation, a +//! length `len`, a capacity `cap`, and an allocator `alloc`. +//! While the exact fields and their order are not specified, a [`Vec`] can be converted to and +//! from these parts using [`Vec::into_raw_parts_with_allocator`], [`Vec::from_raw_parts_in`], +//! [`Vec::into_raw_parts`], and [`Vec::from_raw_parts`], as well as their [`NonNull`]-based +//! versions. //! -//! For zero-sized types (ZSTs), or when the capacity is zero, the `Vec` pointer must be non-null -//! and sufficiently aligned. The recommended way to build a `Vec` of ZSTs if [`vec!`] cannot be -//! used is to use [`ptr::NonNull::dangling`]. +//! The parts of a [`Vec`] have the following invariants: //! +//! * If [`size_of::()`](size_of) and `cap` are non-zero, then: +//! - `ptr` must point to the start of a memory block [*currently allocated*] via `alloc`. +//! - [`Layout::array::(cap)`] must [*fit*] the memory block of `ptr`. In particular, +//! * `T` must have the same alignment as the [`Layout`] `ptr` was allocated with, and +//! * `size_of::() * cap` must be at least the size of the [`Layout`] `ptr` was +//! allocated with, and at most the size of the allocation returned the allocator. +//! - `size_of::() * cap` must be at most [`isize::MAX`]. +//! See the safety documentation of [`pointer::offset`]. +//! * If [`size_of::()`](size_of) or `cap` is zero, then `ptr` must be any sufficiently aligned non-null pointer. +//! +//! NOTE: `ptr` will not be passed to the allocator for deallocation by the destructor of [`Vec`] +//! in this case. Thus, it is unwise to use a pointer acquired from the allocator for this, as +//! overallocated memory may end up being leaked. Use [`ptr::dangling`] instead. +//! * The following must always be satisfied: +//! - `len` needs to be less than or equal to `cap`. +//! - At `ptr`, there are at least `len` valid values of type `T`, laid out contiguously +//! (like an array). These values are considered owned by the [`Vec`]. +//! +//! It is sound to create a [`Vec`] from parts that satisfy these invariants. +//! +//! Conversely, the memory backing `ptr` obtained from [`Vec::as_mut_ptr`] or +//! [`Vec::into_raw_parts_with_allocator`]-like methods may be deallocated +//! using the allocator, with layout [`Layout::array::(cap)`], provided +//! that [`size_of::()`](size_of) or `cap` is non-zero. +//! To automatically handle the case where the allocation is zero-sized, +//! it is recommended to use [`Vec::from_raw_parts_in`], instead of manually +//! deallocating. +//! +//! [*fit*]: crate::alloc::Allocator#memory-fitting +//! [*currently allocated*]: crate::alloc::Allocator#currently-allocated-memory +//! [`allocate`]: crate::alloc::Allocator::allocate //! [`push`]: Vec::push //! [`ptr::NonNull::dangling`]: NonNull::dangling //! [`Layout`]: crate::alloc::Layout -//! [Layout::array]: crate::alloc::Layout::array +//! [`Layout::array::(cap)`]: crate::alloc::Layout::array #![stable(feature = "rust1", since = "1.0.0")] @@ -303,35 +328,13 @@ mod sve_retain; /// /// # Guarantees /// +/// The following section is to be read as an addendum of the +/// [memory layout](self#memory-layout) section. +/// /// Due to its incredibly fundamental nature, `Vec` makes a lot of guarantees /// about its design. This ensures that it's as low-overhead as possible in /// the general case, and can be correctly manipulated in primitive ways -/// by unsafe code. Note that these guarantees refer to an unqualified `Vec`. -/// If additional type parameters are added (e.g., to support custom allocators), -/// overriding their defaults may change the behavior. -/// -/// Most fundamentally, `Vec` is and always will be a (pointer, capacity, length) -/// triplet. No more, no less. The order of these fields is completely -/// unspecified, and you should use the appropriate methods to modify these. -/// The pointer will never be null, so this type is null-pointer-optimized. -/// -/// However, the pointer might not actually point to allocated memory. In particular, -/// if you construct a `Vec` with capacity 0 via [`Vec::new`], [`vec![]`][`vec!`], -/// [`Vec::with_capacity(0)`][`Vec::with_capacity`], or by calling [`shrink_to_fit`] -/// on an empty Vec, it will not allocate memory. Similarly, if you store zero-sized -/// types inside a `Vec`, it will not allocate space for them. *Note that in this case -/// the `Vec` might not report a [`capacity`] of 0*. `Vec` will allocate if and only -/// if [size_of::\]\() * [capacity]\() > 0. In general, `Vec`'s allocation -/// details are very subtle --- if you intend to allocate memory using a `Vec` -/// and use it for something else (either to pass to unsafe code, or to build your -/// own memory-backed collection), be sure to deallocate this memory by using -/// `from_raw_parts` to recover the `Vec` and then dropping it. -/// -/// If a `Vec` *has* allocated memory, then the memory it points to is on the heap -/// (as defined by the allocator Rust is configured to use by default), and its -/// pointer points to [`len`] initialized, contiguous elements in order (what -/// you would see if you coerced it to a slice), followed by [capacity] - [len] -/// logically uninitialized, contiguous elements. +/// by unsafe code. /// /// A vector containing the elements `'a'` and `'b'` with capacity 4 can be /// visualized as below. The top part is the `Vec` struct, it contains a @@ -469,7 +472,7 @@ impl Vec { /// /// 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 zero, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector [will not allocate]. /// /// It is important to note that although the returned vector has the /// minimum *capacity* specified, the vector will have a zero *length*. For @@ -479,11 +482,12 @@ impl Vec { /// If it is important to know the exact allocated capacity of a `Vec`, /// always use the [`capacity`] method after construction. /// - /// For `Vec` where `T` is a zero-sized type, there will be no allocation - /// and the capacity will always be `usize::MAX`. + /// For `Vec` where `T` is a zero-sized type, the vector + /// [will not allocate], and the capacity will always be `usize::MAX`. /// /// [Capacity and reallocation]: #capacity-and-reallocation /// [`capacity`]: Vec::capacity + /// [will not allocate]: self#memory-layout /// /// # Panics /// @@ -529,12 +533,14 @@ impl Vec { /// /// 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 zero, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector [will not allocate]. /// /// # Errors /// /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, /// or if the allocator reports allocation failure. + /// + /// [will not allocate]: self#memory-layout #[inline] #[unstable(feature = "try_with_capacity", issue = "91913")] pub fn try_with_capacity(capacity: usize) -> Result { @@ -545,34 +551,10 @@ impl Vec { /// /// # Safety /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * If `T` is not a zero-sized type and the capacity is nonzero, `ptr` must have - /// been allocated using the global allocator, such as via the [`alloc::alloc`] - /// function. If `T` is a zero-sized type or the capacity is zero, `ptr` need - /// only be non-null and aligned. - /// * `T` needs to have the same alignment as what `ptr` was allocated with, - /// if the pointer is required to be allocated. - /// (`T` having a less strict alignment is not sufficient, the alignment really - /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be - /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes), if - /// nonzero, needs to be the same size as the pointer was allocated with. - /// (Because similar to alignment, [`dealloc`] must be called with the same - /// layout `size`.) - /// * `length` needs to be less than or equal to `capacity`. - /// * The first `length` values must be properly initialized values of type `T`. - /// * `capacity` needs to be the capacity that the pointer was allocated with, - /// if the pointer is required to be allocated. - /// * The allocated size in bytes must be no larger than `isize::MAX`. - /// See the safety documentation of [`pointer::offset`]. - /// - /// These requirements are always upheld by any `ptr` that has been allocated - /// via `Vec`. Other allocation sources are allowed if the invariants are - /// upheld. - /// - /// Violating these may cause problems like corrupting the allocator's + /// The parts must satisfy the invariants described in the [memory layout] section, + /// with allocator [`Global`]. + /// + /// Violating them may cause problems like corrupting the allocator's /// internal data structures. For example it is normally **not** safe /// to build a `Vec` from a pointer to a C `char` array with length /// `size_t`, doing so is only safe if the array was initially allocated by @@ -591,8 +573,7 @@ impl Vec { /// function. /// /// [`String`]: crate::string::String - /// [`alloc::alloc`]: crate::alloc::alloc - /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc + /// [memory layout]: self#memory-layout /// /// # Examples /// @@ -652,29 +633,10 @@ impl Vec { /// /// # Safety /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * `ptr` must have been allocated using the global allocator, such as via - /// the [`alloc::alloc`] function. - /// * `T` needs to have the same alignment as what `ptr` was allocated with. - /// (`T` having a less strict alignment is not sufficient, the alignment really - /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be - /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes) needs - /// to be the same size as the pointer was allocated with. (Because similar to - /// alignment, [`dealloc`] must be called with the same layout `size`.) - /// * `length` needs to be less than or equal to `capacity`. - /// * The first `length` values must be properly initialized values of type `T`. - /// * `capacity` needs to be the capacity that the pointer was allocated with. - /// * The allocated size in bytes must be no larger than `isize::MAX`. - /// See the safety documentation of [`pointer::offset`]. - /// - /// These requirements are always upheld by any `ptr` that has been allocated - /// via `Vec`. Other allocation sources are allowed if the invariants are - /// upheld. - /// - /// Violating these may cause problems like corrupting the allocator's + /// The parts must satisfy the invariants described in the [memory layout] section, + /// with allocator [`Global`]. + /// + /// Violating them may cause problems like corrupting the allocator's /// internal data structures. For example it is normally **not** safe /// to build a `Vec` from a pointer to a C `char` array with length /// `size_t`, doing so is only safe if the array was initially allocated by @@ -693,8 +655,7 @@ impl Vec { /// function. /// /// [`String`]: crate::string::String - /// [`alloc::alloc`]: crate::alloc::alloc - /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc + /// [memory layout]: self#memory-layout /// /// # Examples /// @@ -917,7 +878,7 @@ const impl Vec { /// /// 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 zero, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector [will not allocate]. /// /// It is important to note that although the returned vector has the /// minimum *capacity* specified, the vector will have a zero *length*. For @@ -927,11 +888,12 @@ const impl Vec { /// If it is important to know the exact allocated capacity of a `Vec`, /// always use the [`capacity`] method after construction. /// - /// For `Vec` where `T` is a zero-sized type, there will be no allocation - /// and the capacity will always be `usize::MAX`. + /// For `Vec` where `T` is a zero-sized type, the vector + /// [will not allocate], and the capacity will always be `usize::MAX`. /// /// [Capacity and reallocation]: #capacity-and-reallocation /// [`capacity`]: Vec::capacity + /// [will not allocate]: self#memory-layout /// /// # Panics /// @@ -1069,12 +1031,14 @@ impl Vec { /// /// 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 zero, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector [will not allocate]. /// /// # Errors /// /// Returns an error if the capacity exceeds `isize::MAX` _bytes_, /// or if the allocator reports allocation failure. + /// + /// [will not allocate]: self#memory-layout #[inline] #[unstable(feature = "allocator_ext", issue = "163177", implied_by = "allocator_api")] // #[unstable(feature = "try_with_capacity", issue = "91913")] @@ -1087,28 +1051,9 @@ impl Vec { /// /// # Safety /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * `ptr` must be [*currently allocated*] via the given allocator `alloc`. - /// * `T` needs to have the same alignment as what `ptr` was allocated with. - /// (`T` having a less strict alignment is not sufficient, the alignment really - /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be - /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes) needs - /// to be the same size as the pointer was allocated with. (Because similar to - /// alignment, [`dealloc`] must be called with the same layout `size`.) - /// * `length` needs to be less than or equal to `capacity`. - /// * The first `length` values must be properly initialized values of type `T`. - /// * `capacity` needs to [*fit*] the layout size that the pointer was allocated with. - /// * The allocated size in bytes must be no larger than `isize::MAX`. - /// See the safety documentation of [`pointer::offset`]. - /// - /// These requirements are always upheld by any `ptr` that has been allocated - /// via `Vec`. Other allocation sources are allowed if the invariants are - /// upheld. - /// - /// Violating these may cause problems like corrupting the allocator's + /// The parts must satisfy the invariants described in the [memory layout] section. + /// + /// Violating them may cause problems like corrupting the allocator's /// internal data structures. For example it is **not** safe /// to build a `Vec` from a pointer to a C `char` array with length `size_t`. /// It's also not safe to build one from a `Vec` and its length, because @@ -1123,9 +1068,8 @@ impl Vec { /// function. /// /// [`String`]: crate::string::String - /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc - /// [*currently allocated*]: crate::alloc::Allocator#currently-allocated-memory - /// [*fit*]: crate::alloc::Allocator#memory-fitting + /// [`allocate`]: crate::alloc::Allocator::allocate + /// [memory layout]: self#memory-layout /// /// # Examples /// @@ -1201,28 +1145,9 @@ impl Vec { /// /// # Safety /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * `ptr` must be [*currently allocated*] via the given allocator `alloc`. - /// * `T` needs to have the same alignment as what `ptr` was allocated with. - /// (`T` having a less strict alignment is not sufficient, the alignment really - /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be - /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (i.e. the allocated size in bytes) needs - /// to be the same size as the pointer was allocated with. (Because similar to - /// alignment, [`dealloc`] must be called with the same layout `size`.) - /// * `length` needs to be less than or equal to `capacity`. - /// * The first `length` values must be properly initialized values of type `T`. - /// * `capacity` needs to [*fit*] the layout size that the pointer was allocated with. - /// * The allocated size in bytes must be no larger than `isize::MAX`. - /// See the safety documentation of [`pointer::offset`]. - /// - /// These requirements are always upheld by any `ptr` that has been allocated - /// via `Vec`. Other allocation sources are allowed if the invariants are - /// upheld. - /// - /// Violating these may cause problems like corrupting the allocator's + /// The parts must satisfy the invariants described in the [memory layout] section. + /// + /// Violating them may cause problems like corrupting the allocator's /// internal data structures. For example it is **not** safe /// to build a `Vec` from a pointer to a C `char` array with length `size_t`. /// It's also not safe to build one from a `Vec` and its length, because @@ -1238,8 +1163,7 @@ impl Vec { /// /// [`String`]: crate::string::String /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc - /// [*currently allocated*]: crate::alloc::Allocator#currently-allocated-memory - /// [*fit*]: crate::alloc::Allocator#memory-fitting + /// [memory layout]: self#memory-layout /// /// # Examples /// @@ -1899,7 +1823,7 @@ impl Vec { } /// Returns a raw pointer to the vector's buffer, or a dangling raw pointer - /// valid for zero sized reads if the vector didn't allocate. + /// valid for zero sized reads if the vector [didn't allocate]. /// /// The caller must ensure that the vector outlives the pointer this /// function returns, or else it will end up dangling. @@ -1951,6 +1875,7 @@ impl Vec { /// [`as_mut_ptr`]: Vec::as_mut_ptr /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null + /// [didn't allocate]: self#memory-layout #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] #[rustc_never_returns_null_ptr] @@ -1963,7 +1888,7 @@ impl Vec { } /// Returns a raw mutable pointer to the vector's buffer, or a dangling - /// raw pointer valid for zero sized reads if the vector didn't allocate. + /// raw pointer valid for zero sized reads if the vector [didn't allocate]. /// /// The caller must ensure that the vector outlives the pointer this /// function returns, or else it will end up dangling. @@ -2035,6 +1960,7 @@ impl Vec { /// [`as_non_null`]: Vec::as_non_null /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc /// [`ManuallyDrop`]: core::mem::ManuallyDrop + /// [didn't allocate]: self#memory-layout #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] #[rustc_never_returns_null_ptr] @@ -2047,7 +1973,7 @@ impl Vec { } /// Returns a `NonNull` pointer to the vector's buffer, or a dangling - /// `NonNull` pointer valid for zero sized reads if the vector didn't allocate. + /// `NonNull` pointer valid for zero sized reads if the vector [didn't allocate]. /// /// The caller must ensure that the vector outlives the pointer this /// function returns, or else it will end up dangling. @@ -2102,6 +2028,7 @@ impl Vec { /// [`as_mut_ptr`]: Vec::as_mut_ptr /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null + /// [didn't allocate]: self#memory-layout #[unstable(feature = "vec_as_non_null", issue = "157843")] #[rustc_const_unstable(feature = "vec_as_non_null", issue = "157843")] #[rustc_as_ptr] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 530af0c3af656..aeabacb0d6364 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1135,7 +1135,7 @@ marker_impls! { } marker_impls! { - #[unstable(feature = "unsized_const_params", issue = "95174")] + #[unstable(feature = "unsized_const_params", issue = "128028")] #[unstable_feature_bound(unsized_const_params)] ConstParamTy_ for str, diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 7fb976d5ef3da..605ad25270a63 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -852,102 +852,175 @@ fn file_test_windows_fileext_trait_case_1() { #[test] #[cfg(windows)] fn file_test_windows_fileext_trait_case_2() { + use crate::cell::RefCell; use crate::os::windows::fs::FileExt; // Test when seek_read(), seek_write() return Ok(0) struct MockFile { expected_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(expected_offset: u64) -> Self { + Self { expected_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } } impl FileExt for MockFile { fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); Ok(0) } fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); Ok(0) } } - { - let mock_file = MockFile { expected_offset: 0 }; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), - io::ErrorKind::UnexpectedEof - ); - assert_eq!(mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), io::ErrorKind::WriteZero); - } - - { - let mock_file = MockFile { expected_offset: 420 }; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), - io::ErrorKind::UnexpectedEof - ); - assert_eq!( - mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), - io::ErrorKind::WriteZero - ); + let mut buf = [0; 256]; + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_read_exact(&mut buf, offset).unwrap_err().kind(), + ErrorKind::UnexpectedEof + ); + assert_eq!(*mock_file.read_calls.borrow(), 1); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_write_all(&buf, offset).unwrap_err().kind(), + ErrorKind::WriteZero + ); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), 1); + } } } #[test] #[cfg(windows)] fn file_test_windows_fileext_trait_case_3() { + use crate::cell::RefCell; use crate::os::windows::fs::FileExt; - // Test that Err other than io::ErrorKind::Interrupted are propagated up. + // Test that Err are propagated up. struct MockFile { expected_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(expected_offset: u64) -> Self { + Self { expected_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } } impl FileExt for MockFile { fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); - Err(io::Error::new(io::ErrorKind::PermissionDenied, "seek_read")) + Err(io::Error::new(ErrorKind::PermissionDenied, "seek_read")) } fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); - Err(io::Error::new(io::ErrorKind::ConnectionRefused, "seek_write")) + Err(io::Error::new(ErrorKind::ConnectionRefused, "seek_write")) } } - { - let mock_file = MockFile { expected_offset: 0 }; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), - io::ErrorKind::PermissionDenied - ); - assert_eq!( - mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), - io::ErrorKind::ConnectionRefused - ); + let mut buf = [0; 256]; + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_read_exact(&mut buf, offset).unwrap_err().kind(), + ErrorKind::PermissionDenied + ); + assert_eq!(*mock_file.read_calls.borrow(), 1); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_write_all(&buf, offset).unwrap_err().kind(), + ErrorKind::ConnectionRefused + ); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), 1); + } } +} - { - let mock_file = MockFile { expected_offset: 420 }; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), - io::ErrorKind::PermissionDenied - ); - assert_eq!( - mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), - io::ErrorKind::ConnectionRefused - ); +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_4() { + use crate::cell::RefCell; + use crate::os::windows::fs::FileExt; + + // Test that Err ErrorKind::Interrupted are propagated up (unlike Unix versions) + struct MockFile { + expected_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(expected_offset: u64) -> Self { + Self { expected_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } + } + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; + assert_eq!(offset, self.expected_offset); + Err(io::Error::new(ErrorKind::Interrupted, "seek_read")) + } + + fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; + assert_eq!(offset, self.expected_offset); + Err(io::Error::new(ErrorKind::Interrupted, "seek_write")) + } + } + + let mut buf = [0; 256]; + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_read_exact(&mut buf, offset).unwrap_err().kind(), + ErrorKind::Interrupted + ); + assert_eq!(*mock_file.read_calls.borrow(), 1); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + assert_eq!( + mock_file.seek_write_all(&buf, offset).unwrap_err().kind(), + ErrorKind::Interrupted + ); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), 1); + } } - // FIXME: Cover io::ErrorKind::Interrupted, but don't infinite loop ;) } #[test] #[cfg(windows)] -fn file_test_windows_fileext_trait_case_4() { +fn file_test_windows_fileext_trait_case_5() { + use crate::cell::RefCell; use crate::os::windows::fs::FileExt; const MSG: &[u8] = @@ -957,10 +1030,19 @@ fn file_test_windows_fileext_trait_case_4() { // seek_write(), respectively. struct MockFile { expected_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(expected_offset: u64) -> Self { + Self { expected_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } } impl FileExt for MockFile { fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); assert_eq!(buf.len(), MSG.len()); assert_eq!(buf, &[0; MSG.len()]); @@ -969,6 +1051,7 @@ fn file_test_windows_fileext_trait_case_4() { } fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; assert_eq!(offset, self.expected_offset); assert_eq!(buf.len(), MSG.len()); assert_eq!(buf, MSG); @@ -976,26 +1059,28 @@ fn file_test_windows_fileext_trait_case_4() { } } - { - let mock_file = MockFile { expected_offset: 0 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 0)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 0)); - } - - { - let mock_file = MockFile { expected_offset: 420 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 420)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 420)); + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, offset)); + assert_eq!(&buf, MSG); + assert_eq!(*mock_file.read_calls.borrow(), 1); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + check!(mock_file.seek_write_all(MSG, offset)); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), 1); + } } } #[test] #[cfg(windows)] -fn file_test_windows_fileext_trait_case_5() { +fn file_test_windows_fileext_trait_case_6() { + use crate::cell::RefCell; use crate::os::windows::fs::FileExt; const MSG: &[u8] = @@ -1004,36 +1089,47 @@ fn file_test_windows_fileext_trait_case_5() { // Test pathological case where seek_read(), seek_write() only do 1 byte per call, return Ok(1) struct MockFile { base_offset: u64, + read_calls: RefCell, + write_calls: RefCell, + } + + impl MockFile { + fn new(base_offset: u64) -> Self { + Self { base_offset, read_calls: RefCell::new(0), write_calls: RefCell::new(0) } + } } impl FileExt for MockFile { fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + *self.read_calls.borrow_mut() += 1; let offset = (offset - self.base_offset) as usize; buf[0..1].copy_from_slice(&MSG[offset..offset + 1]); Ok(1) } fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + *self.write_calls.borrow_mut() += 1; let offset = (offset - self.base_offset) as usize; assert_eq!(buf[0..1], MSG[offset..offset + 1]); Ok(1) } } - { - let mock_file = MockFile { base_offset: 0 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 0)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 0)); - } - - { - let mock_file = MockFile { base_offset: 420 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 420)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 420)); + for offset in [0, 1, 420] { + { + let mock_file = MockFile::new(offset); + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, offset)); + assert_eq!(&buf, MSG); + assert_eq!(*mock_file.read_calls.borrow(), MSG.len()); + assert_eq!(*mock_file.write_calls.borrow(), 0); + } + { + let mock_file = MockFile::new(offset); + check!(mock_file.seek_write_all(MSG, offset)); + assert_eq!(*mock_file.read_calls.borrow(), 0); + assert_eq!(*mock_file.write_calls.borrow(), MSG.len()); + } } } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 0848f97e69d32..527671442fb72 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -95,27 +95,6 @@ const fn stderr_raw() -> StderrRaw { StderrRaw(stdio::Stderr::new()) } -#[cfg(windows)] -impl StdoutRaw { - /// Starts a new lock session: stream state cached per lock session (the - /// handle and its console mode) is re-queried at the next write, so that - /// changing the process stdio handles (e.g. with `SetStdHandle`) between - /// lock sessions keeps working. - #[inline] - fn refresh(&mut self) { - self.0.refresh(); - } -} - -#[cfg(windows)] -impl StderrRaw { - /// See `StdoutRaw::refresh`. - #[inline] - fn refresh(&mut self) { - self.0.refresh(); - } -} - impl Read for StdinRaw { fn read(&mut self, buf: &mut [u8]) -> io::Result { handle_ebadf(self.0.read(buf), || Ok(0)) @@ -790,19 +769,7 @@ impl Stdout { // Locks this handle with 'static lifetime. This depends on the // implementation detail that the underlying `ReentrantMutex` is // static. - let lock = StdoutLock { inner: self.inner.lock() }; - // A new lock session begins, so let Windows re-query cached stream - // state at the next write; other platforms cache nothing, and skip - // this entirely so their `lock()` is unchanged. Skipped also if the - // cell is already borrowed (a nested lock during an ongoing write); - // such a lock is part of the outer session anyway. - #[cfg(windows)] - { - if let Ok(mut w) = lock.inner.try_borrow_mut() { - w.get_mut().refresh(); - } - } - lock + StdoutLock { inner: self.inner.lock() } } } @@ -1034,15 +1001,7 @@ impl Stderr { // Locks this handle with 'static lifetime. This depends on the // implementation detail that the underlying `ReentrantMutex` is // static. - let lock = StderrLock { inner: self.inner.lock() }; - // See `Stdout::lock`: a new lock session begins (Windows only). - #[cfg(windows)] - { - if let Ok(mut w) = lock.inner.try_borrow_mut() { - w.refresh(); - } - } - lock + StderrLock { inner: self.inner.lock() } } } diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index c10766470e873..4c2506ffae66c 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -69,9 +69,6 @@ pub trait FileExt { /// /// # Errors /// - /// If this function encounters an error of the kind - /// [`io::ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. /// /// If this function encounters an "end of file" before completely filling /// the buffer, it returns an error of the kind [`io::ErrorKind::UnexpectedEof`]. @@ -113,7 +110,6 @@ pub trait FileExt { buf = &mut buf[n..]; offset += n as u64; } - Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } @@ -203,7 +199,6 @@ pub trait FileExt { let prev_written = buf.written(); match self.seek_read_buf(buf.reborrow(), offset) { Ok(()) => {} - Err(e) if e.is_interrupted() => {} Err(e) => return Err(e), } let n = buf.written() - prev_written; @@ -264,8 +259,7 @@ pub trait FileExt { /// /// # Errors /// - /// This function will return the first error of - /// non-[`io::ErrorKind::Interrupted`] kind that [`seek_write`] returns. + /// This function will return the first error that [`seek_write`] returns. /// /// [`seek_write`]: FileExt::seek_write /// @@ -298,7 +292,6 @@ pub trait FileExt { buf = &buf[n..]; offset += n as u64 } - Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } diff --git a/library/std/src/sys/io/is_terminal/windows.rs b/library/std/src/sys/io/is_terminal/windows.rs index b0c718d71f9f3..6d66e93087911 100644 --- a/library/std/src/sys/io/is_terminal/windows.rs +++ b/library/std/src/sys/io/is_terminal/windows.rs @@ -1,4 +1,5 @@ use crate::ffi::c_void; +use crate::mem::MaybeUninit; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; use crate::sys::c; @@ -12,10 +13,44 @@ fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { return false; } - let mut out = 0; - if unsafe { c::GetConsoleMode(handle.as_raw_handle(), &mut out) != 0 } { - // False positives aren't possible. If we got a console then we definitely have a console. - return true; + cfg_select! { + target_vendor = "win7" => { + let mut out = 0; + if unsafe { c::GetConsoleMode(handle.as_raw_handle(), &mut out) != 0 } { + // False positives aren't possible. If we got a console then we definitely have a console. + return true; + } + + // Early return if the handle is not FILE_TYPE_PIPE, implying it cannot be an msys PTY. + if unsafe { c::GetFileType(handle.as_raw_handle()) != 0x0003 } { + return false; + } + } + _ => unsafe { + // `GetConsoleMode` incurs a remote RPC round-trip (= bad), so it's best avoided. + // `NtQueryVolumeInformationFile` lets us test the handle directly. + // The required `FILE_DEVICE_CONSOLE` type was introduced in Windows 8. + // Fun fact: `GetFileType` uses `NtQueryVolumeInformationFile` internally. + let mut io_status = MaybeUninit::::uninit(); + let mut device_info = MaybeUninit::::uninit(); + let status = c::NtQueryVolumeInformationFile( + handle.as_raw_handle(), + io_status.as_mut_ptr(), + device_info.as_mut_ptr().cast(), + size_of_val(&device_info) as u32, + c::FileFsDeviceInformation, + ); + if status < 0 { + return false; + } + + match device_info.assume_init_ref().DeviceType { + c::FILE_DEVICE_CONSOLE => return true, + // If it's a named pipe, it may be an msys PTY. + c::FILE_DEVICE_NAMED_PIPE => {} // aka FILE_TYPE_PIPE + _ => return false, + } + }, } // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty. @@ -23,11 +58,6 @@ fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { } fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool { - // Early return if the handle is not a pipe. - if unsafe { c::GetFileType(handle.as_raw_handle()) != c::FILE_TYPE_PIPE } { - return false; - } - /// Mirrors [`FILE_NAME_INFO`], giving it a fixed length that we can stack /// allocate /// diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 25031968a7e02..67345d183a2d2 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -20,6 +20,9 @@ pub type WCHAR = u16; pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::without_provenance_mut(-1i32 as _); +pub const FILE_DEVICE_NAMED_PIPE: u32 = 0x00000011; +pub const FILE_DEVICE_CONSOLE: u32 = 0x00000050; + // https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170 pub const EXIT_SUCCESS: u32 = 0; pub const EXIT_FAILURE: u32 = 1; @@ -188,9 +191,6 @@ pub const FILE_CURRENT: u32 = cu(windows_sys::FILE_CURRENT); pub const FSCTL_GET_REPARSE_POINT: u32 = cu(windows_sys::FSCTL_GET_REPARSE_POINT); pub const FSCTL_SET_REPARSE_POINT: u32 = cu(windows_sys::FSCTL_SET_REPARSE_POINT); -// GetFileType return value (DWORD) -pub const FILE_TYPE_PIPE: u32 = cu(windows_sys::FILE_TYPE_PIPE); - // GetACP return value (UINT) pub const CP_UTF8: u32 = windows_sys::CP_UTF8.cast_unsigned(); @@ -473,6 +473,7 @@ cfg_select! { target_vendor = "uwp" => { windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : ACCESS_MASK, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : u32, shareaccess : u32, createdisposition : u32, createoptions : u32, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : ACCESS_MASK, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS); + windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtQueryVolumeInformationFile(filehandle : HANDLE, iostatusblock : *mut IO_STATUS_BLOCK, fsinformation : *mut core::ffi::c_void, length : u32, fsinformationclass : FS_INFORMATION_CLASS) -> NTSTATUS); windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_link::link_raw_dylib!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32); diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 70af36f27907e..71f94954b1356 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2032,6 +2032,7 @@ FILE_FLAG_RANDOM_ACCESS FILE_FLAG_SEQUENTIAL_SCAN FILE_FLAG_SESSION_AWARE FILE_FLAG_WRITE_THROUGH +FILE_FS_DEVICE_INFORMATION FILE_GENERIC_EXECUTE FILE_GENERIC_READ FILE_GENERIC_WRITE @@ -2084,7 +2085,6 @@ FILE_SYNCHRONOUS_IO_NONALERT FILE_TRAVERSE FILE_TYPE_CHAR FILE_TYPE_DISK -FILE_TYPE_PIPE FILE_TYPE_REMOTE FILE_TYPE_UNKNOWN FILE_WRITE_ATTRIBUTES @@ -2100,6 +2100,7 @@ FileCompressionInfo FileDispositionInfo FileDispositionInfoEx FileEndOfFileInfo +FileFsDeviceInformation FileFullDirectoryInfo FileFullDirectoryRestartInfo FileIdBothDirectoryInfo @@ -2303,6 +2304,7 @@ NO_ERROR NORMAL_PRIORITY_CLASS NtCreateFile NtOpenFile +NtQueryVolumeInformationFile NtReadFile NtSetInformationFile NtWriteFile diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 0c865494a6e7c..ae08f790bb40a 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -78,6 +78,7 @@ windows_link::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : windows_link::link!("kernel32.dll" "system" fn MultiByteToWideChar(codepage : u32, dwflags : u32, lpmultibytestr : *const i8, cbmultibyte : i32, lpwidecharstr : PWSTR, cchwidechar : i32) -> i32); windows_link::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : ACCESS_MASK, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : u32, shareaccess : u32, createdisposition : u32, createoptions : u32, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); windows_link::link!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : ACCESS_MASK, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS); +windows_link::link!("ntdll.dll" "system" fn NtQueryVolumeInformationFile(filehandle : HANDLE, iostatusblock : *mut IO_STATUS_BLOCK, fsinformation : *mut core::ffi::c_void, length : u32, fsinformationclass : FS_INFORMATION_CLASS) -> NTSTATUS); windows_link::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_link::link!("ntdll.dll" "system" fn NtSetInformationFile(filehandle : HANDLE, iostatusblock : *mut IO_STATUS_BLOCK, fileinformation : *const core::ffi::c_void, length : u32, fileinformationclass : FILE_INFORMATION_CLASS) -> NTSTATUS); windows_link::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); @@ -2544,6 +2545,12 @@ pub const FILE_FLAG_RANDOM_ACCESS: i32 = 268435456; pub const FILE_FLAG_SEQUENTIAL_SCAN: i32 = 134217728; pub const FILE_FLAG_SESSION_AWARE: i32 = 8388608; pub const FILE_FLAG_WRITE_THROUGH: u32 = 2147483648; +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct FILE_FS_DEVICE_INFORMATION { + pub DeviceType: u32, + pub Characteristics: u32, +} pub const FILE_GENERIC_EXECUTE: i32 = 1179808; pub const FILE_GENERIC_READ: i32 = 1179785; pub const FILE_GENERIC_WRITE: i32 = 1179926; @@ -2678,7 +2685,6 @@ pub const FILE_SYNCHRONOUS_IO_NONALERT: i32 = 32; pub const FILE_TRAVERSE: i32 = 32; pub const FILE_TYPE_CHAR: i32 = 2; pub const FILE_TYPE_DISK: i32 = 1; -pub const FILE_TYPE_PIPE: i32 = 3; pub const FILE_TYPE_REMOTE: i32 = 32768; pub const FILE_TYPE_UNKNOWN: i32 = 0; pub const FILE_WRITE_ATTRIBUTES: i32 = 256; @@ -2718,6 +2724,7 @@ pub const FORMAT_MESSAGE_IGNORE_INSERTS: i32 = 512; pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: i32 = 8014; pub const FSCTL_GET_REPARSE_POINT: i32 = 589992; pub const FSCTL_SET_REPARSE_POINT: i32 = 589988; +pub type FS_INFORMATION_CLASS = i32; pub const FileAlignmentInfo: FILE_INFO_BY_HANDLE_CLASS = 17; pub const FileAllocationInfo: FILE_INFO_BY_HANDLE_CLASS = 5; pub const FileAttributeTagInfo: FILE_INFO_BY_HANDLE_CLASS = 9; @@ -2727,6 +2734,7 @@ pub const FileCompressionInfo: FILE_INFO_BY_HANDLE_CLASS = 8; pub const FileDispositionInfo: FILE_INFO_BY_HANDLE_CLASS = 4; pub const FileDispositionInfoEx: FILE_INFO_BY_HANDLE_CLASS = 21; pub const FileEndOfFileInfo: FILE_INFO_BY_HANDLE_CLASS = 6; +pub const FileFsDeviceInformation: FS_INFORMATION_CLASS = 4; pub const FileFullDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 14; pub const FileFullDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 15; pub const FileIdBothDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 10; diff --git a/library/std/src/sys/stdio/windows.rs b/library/std/src/sys/stdio/windows.rs index d9c9290ed21a2..82a4424d46989 100644 --- a/library/std/src/sys/stdio/windows.rs +++ b/library/std/src/sys/stdio/windows.rs @@ -22,35 +22,12 @@ pub struct Stdin { pub struct Stdout { incomplete_utf8: IncompleteUtf8, - write_mode: Option, } pub struct Stderr { incomplete_utf8: IncompleteUtf8, - write_mode: Option, } -/// How to write to a standard stream, cached for the duration of a lock -/// session (see `Stdout::refresh`). Re-querying this on every lock -/// acquisition keeps `SetStdHandle` calls made between lock sessions working -/// (see #40490), while a held lock skips the per-write console queries that -/// otherwise dominate bulk writes (see #154071). -#[derive(Clone, Copy)] -enum WriteMode { - /// The stream is a pipe or file, or a console using the UTF-8 code page: - /// bytes are written out unchanged. - Passthrough(c::HANDLE), - /// The stream is a console using a non-UTF-8 code page: data is converted - /// to UTF-16 and written with `WriteConsoleW`. - Utf16Console(c::HANDLE), -} - -// SAFETY: a `HANDLE` stored here is only an OS handle value for a standard -// stream (it is never dereferenced), and handle values are valid -// process-wide, on any thread. -unsafe impl Send for WriteMode {} -unsafe impl Sync for WriteMode {} - struct IncompleteUtf8 { bytes: [u8; 4], len: u8, @@ -100,62 +77,56 @@ pub fn get_handle(handle_id: u32) -> io::Result { } } -fn is_console(handle: c::HANDLE) -> bool { - // `GetConsoleMode` will return false (0) if this is a pipe (we don't care about the reported - // mode). This will only detect Windows Console, not other terminals connected to a pipe like - // MSYS. Which is exactly what we need, as only Windows Console needs a conversion to UTF-16. - let mut mode = 0; - unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } -} - -/// Returns true if the attached console's code page is currently UTF-8. +/// Returns true if `handle` refers to a proper console (aka conhost). #[cfg(not(target_vendor = "win7"))] -fn is_utf8_console() -> bool { - unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 } -} - -#[cfg(target_vendor = "win7")] -fn is_utf8_console() -> bool { - // Windows 7 has a fun "feature" where WriteFile on a console handle will return - // the number of UTF-16 code units written and not the number of bytes from the input string. - // So we always claim the console isn't UTF-8 to trigger the WriteConsole fallback code. - false +fn is_console(handle: c::HANDLE) -> bool { + cfg_select! { + target_vendor = "win7" => { + // `GetConsoleMode` will return false (0) if this is a pipe (we don't care about the reported + // mode). This will only detect Windows Console, not other terminals connected to a pipe like + // MSYS. Which is exactly what we need, as only Windows Console needs a conversion to UTF-16. + let mut mode = 0; + unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } + } + _ => unsafe { + // `GetConsoleMode` incurs a remote RPC round-trip (= bad), so it's best avoided. + // `NtQueryVolumeInformationFile` lets us test the handle directly. + // The required `FILE_DEVICE_CONSOLE` type was introduced in Windows 8. + // Fun fact: `GetFileType` uses `NtQueryVolumeInformationFile` internally. + let mut io_status = MaybeUninit::::uninit(); + let mut device_info = MaybeUninit::::uninit(); + let status = c::NtQueryVolumeInformationFile( + handle, + io_status.as_mut_ptr(), + device_info.as_mut_ptr().cast(), + size_of_val(&device_info) as u32, + c::FileFsDeviceInformation, + ); + status >= 0 && device_info.assume_init_ref().DeviceType == c::FILE_DEVICE_CONSOLE + }, + } } -fn write( - handle_id: u32, - data: &[u8], - incomplete_utf8: &mut IncompleteUtf8, - write_mode: &mut Option, -) -> io::Result { +fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result { if data.is_empty() { return Ok(0); } - let mode = match *write_mode { - Some(mode) => mode, - None => { - let handle = get_handle(handle_id)?; - let mode = if !is_console(handle) || is_utf8_console() { - WriteMode::Passthrough(handle) - } else { - WriteMode::Utf16Console(handle) - }; - // Only cache success; if there is no handle, keep erroring on - // every write like before. - *write_mode = Some(mode); - mode - } - }; + let handle = get_handle(handle_id)?; - match mode { - WriteMode::Passthrough(handle) => unsafe { + // At the time of writing, console IO on Windows is high latency: + // Windows has no TTY buffers, making every call a fully synchronous RPC. + // So, while always deferring to `write_console_utf16` wastes valuable heap for + // our UTF16 conversion, it handily beats doing 2 (or more) costly RPC round-trips. + if !is_console(handle) { + unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.write(data); let _ = handle.into_raw_handle(); // Don't close the handle ret - }, - WriteMode::Utf16Console(handle) => write_console_utf16(data, incomplete_utf8, handle), + } + } else { + write_console_utf16(data, incomplete_utf8, handle) } } @@ -481,21 +452,13 @@ impl IncompleteUtf8 { impl Stdout { pub const fn new() -> Stdout { - Stdout { incomplete_utf8: IncompleteUtf8::new(), write_mode: None } - } - - /// Forgets the cached stream state, so the next write re-queries the OS. - /// Called when a new lock session begins, so that changing the process - /// stdio handles (e.g. `SetStdHandle`) between lock sessions keeps - /// working (see #40490). - pub fn refresh(&mut self) { - self.write_mode = None; + Stdout { incomplete_utf8: IncompleteUtf8::new() } } } impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8, &mut self.write_mode) + write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8) } fn flush(&mut self) -> io::Result<()> { @@ -505,18 +468,13 @@ impl io::Write for Stdout { impl Stderr { pub const fn new() -> Stderr { - Stderr { incomplete_utf8: IncompleteUtf8::new(), write_mode: None } - } - - /// See `Stdout::refresh`. - pub fn refresh(&mut self) { - self.write_mode = None; + Stderr { incomplete_utf8: IncompleteUtf8::new() } } } impl io::Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result { - write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8, &mut self.write_mode) + write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8) } fn flush(&mut self) -> io::Result<()> { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index b3a6817b5d467..0362cb82dc6bf 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -254,7 +254,7 @@ fn make_win_dist(plat_root: &Path, target: TargetSelection, builder: &Builder<'_ } else { "gcc.exe" }; - let target_tools = [compiler, "ld.exe", "dlltool.exe", "libwinpthread-1.dll"]; + let target_tools = [compiler, "ld.exe", "libwinpthread-1.dll"]; // Libraries necessary to link the windows-gnu toolchains. // System libraries will be preferred if they are available (see #67429). diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 0f85e6d00e36b..4934b66be8971 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -98,13 +98,6 @@ It takes one of the following values: For example, for gcc flavor linkers, this issues the `-nodefaultlibs` flag to the linker. -## dlltool - -On `windows-gnu` targets, this flag controls which dlltool `rustc` invokes to -generate import libraries when using the [`raw-dylib` link kind](../../reference/items/external-blocks.md#the-link-attribute). -It takes a path to [the dlltool executable](https://sourceware.org/binutils/docs/binutils/dlltool.html). -If this flag is not specified, a dlltool executable will be inferred based on -the host environment and target. ## dwarf-version @@ -261,7 +254,7 @@ You can pass a comma-separated list of self-contained components, individually e (`+component`) or disabled (`-component`). Currently, only the `linker` granular option is stabilized, and only on the `x86_64-unknown-linux-gnu` target: -- `linker`: toggle the usage of self-contained linker binaries (linker, dlltool, and their necessary libraries) +- `linker`: toggle the usage of self-contained linker binaries (linker and its necessary libraries) Note that only the `-linker` opt-out is stable on the `x86_64-unknown-linux-gnu` target: `+linker` is already the default on this target. diff --git a/src/doc/unstable-book/src/compiler-flags/codegen-options.md b/src/doc/unstable-book/src/compiler-flags/codegen-options.md index efde41040184a..997b04d522ad2 100644 --- a/src/doc/unstable-book/src/compiler-flags/codegen-options.md +++ b/src/doc/unstable-book/src/compiler-flags/codegen-options.md @@ -43,8 +43,7 @@ instead of those in the system. The stable boolean values for this flag are coar - `crto`: CRT objects (e.g. on `windows-gnu`, `musl`, `wasi` targets) - `libc`: libc static library (e.g. on `musl`, `wasi` targets) - `unwind`: libgcc/libunwind (e.g. on `windows-gnu`, `fuchsia`, `fortanix`, `gnullvm` targets) -- `linker`: linker, dlltool, and their necessary libraries (e.g. on `windows-gnu` and for - `rust-lld`) +- `linker`: linker and its necessary libraries (e.g. for `rust-lld`) - `sanitizers`: sanitizer runtime libraries - `mingw`: other MinGW libs and Windows import libs diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index f6d5ada07f364..544d57d92613c 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -960,11 +960,17 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: )?; if let Some(list) = must_implement_one_of_functions.as_deref() { - write!( - w, - "
At least one of the `{}` methods is required.
", - fmt::from_fn(|f| list.iter().joined("`, `", f)), + w.write_str( + "\ +
At least one of the ", )?; + for (pos, method) in list.iter().enumerate() { + if pos > 0 { + w.write_str(", ")?; + } + write!(w, "{method}")?; + } + w.write_str(" methods is required.
")?; } for m in required_methods { diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 3befbf2cff8ff..e0bd1fc9792e6 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -337,30 +337,39 @@ fn sidebar_trait<'a>( ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const), ("required-associated-types", "Required Associated Types", req_assoc), ("provided-associated-types", "Provided Associated Types", prov_assoc), - ("required-methods", "Required Methods", req_method), - ("provided-methods", "Provided Methods", prov_method), - ("foreign-impls", "Implementations on Foreign Types", foreign_impls), ] .into_iter() .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)), ); + + let tcx = cx.tcx(); + // Because of `rustc_must_implement_one_of`, we might have a trait with no "required methods" + // but still want the link in the sidebar, so we handle it on its own. + let mut req_method_link = + LinkBlock::new(Link::new("required-methods", "Required Methods"), "", req_method); + let rustc_middle::ty::TraitDef { must_implement_one_of, .. } = tcx.trait_def(t.def_id); + req_method_link.force_render = must_implement_one_of.is_some(); + blocks.push(req_method_link); + + blocks.push(LinkBlock::new(Link::new("provided-methods", "Provided Methods"), "", prov_method)); + sidebar_assoc_items(cx, it, blocks, deref_id_map); - // Move the foreign impls block after dyn compatibility note to match the order of the headings - // in the main content. - let foreign_impls_block = blocks.pop_if(|b| b.heading.href == "foreign-impls"); - if !t.is_dyn_compatible(cx.tcx()) { + if !t.is_dyn_compatible(tcx) { blocks.push(LinkBlock::forced( Link::new("dyn-compatibility", "Dyn Compatibility"), "dyn-compatibility-note", )); } - if let Some(foreign_impls_block) = foreign_impls_block { - blocks.push(foreign_impls_block); - } + + blocks.push(LinkBlock::new( + Link::new("foreign-impls", "Implementations on Foreign Types"), + "", + foreign_impls, + )); blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl")); - if t.is_auto(cx.tcx()) { + if t.is_auto(tcx) { blocks.push(LinkBlock::forced( Link::new("synthetic-implementors", "Auto Implementors"), "impl-auto", diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 5701982840bac..022fd9b3c4aae 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -813,18 +813,10 @@ impl Config { || (arch == "i586" && self.target.starts_with("i586-")) } - pub(crate) fn matches_os(&self, os: &str) -> bool { - self.target_cfg().os == os - } - pub(crate) fn matches_env(&self, env: &str) -> bool { self.target_cfg().env == env } - pub(crate) fn matches_abi(&self, abi: &str) -> bool { - self.target_cfg().abi == abi - } - #[cfg_attr(not(test), expect(dead_code, reason = "only used by tests for `ignore-{family}`"))] pub(crate) fn matches_family(&self, family: &str) -> bool { self.target_cfg().families.iter().any(|f| f == family) diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 732f4ab275dd7..8b3070e69cccb 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -172,7 +172,6 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-backends", "needs-crate-type", "needs-deterministic-layouts", - "needs-dlltool", "needs-dynamic-linking", "needs-enzyme", "needs-force-clang-based-tests", diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index ce67b11fba211..289fec6cd9e0c 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -299,11 +299,6 @@ pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditio }, ignore_reason: "ignored on targets without Rust's LLD", }, - Need { - name: "needs-dlltool", - condition: find_dlltool(config), - ignore_reason: "ignored when dlltool for the current architecture is not present", - }, Need { name: "needs-git-hash", condition: config.git_hash, @@ -373,29 +368,6 @@ pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditio PreparedNeedsConditions { simple_needs } } -fn find_dlltool(config: &Config) -> bool { - let path = std::env::var_os("PATH").expect("missing PATH environment variable"); - let path = std::env::split_paths(&path).collect::>(); - - // dlltool is used ony by GNU based `*-*-windows-gnu` - if !(config.matches_os("windows") && config.matches_env("gnu") && config.matches_abi("")) { - return false; - } - - // On Windows, dlltool.exe is used for all architectures. - // For non-Windows, there are architecture specific dlltool binaries. - let dlltool_found = if cfg!(windows) { - path.iter().any(|dir| dir.join("dlltool.exe").is_file()) - } else if config.matches_arch("i686") { - path.iter().any(|dir| dir.join("i686-w64-mingw32-dlltool").is_file()) - } else if config.matches_arch("x86_64") { - path.iter().any(|dir| dir.join("x86_64-w64-mingw32-dlltool").is_file()) - } else { - false - }; - dlltool_found -} - // FIXME(#135928): this is actually not quite right because this detection is run on the **host**. // This however still helps the case of windows -> windows local development in case symlinks are // not available. diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 6facfe362c4eb..03cfa1f3129fc 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -721,7 +721,6 @@ fn matches_os() { ]; for (target, os) in oss { let config = cfg().target(target).build(); - assert!(config.matches_os(os), "{target} {os}"); assert!(check_ignore(&config, &format!("//@ ignore-{os}"))); } } @@ -746,7 +745,6 @@ fn matches_abi() { let abis = [("x86_64-unknown-linux-gnux32", "x32"), ("arm-unknown-linux-gnueabi", "eabi")]; for (target, abi) in abis { let config: Config = cfg().target(target).build(); - assert!(config.matches_abi(abi), "{target} {abi}"); assert!(check_ignore(&config, &format!("//@ ignore-{abi}"))); } } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 83d268a75d2b5..e3b0f3d573bf9 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -1190,6 +1190,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. + "NtQueryVolumeInformationFile" if this.frame_in_std() => { + let [_handle, _iosb, _info, _length, _class] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, *_, *_, u32, i32) -> i32), + (link_name, abi, args), + )?; + // Return an error to make std think that nothing is a console. + // This means the normal NtWriteFile is used for stdout/stderr, greatly simplifying our lives. + let not_implemented = this.eval_windows("c", "STATUS_NOT_IMPLEMENTED"); + this.write_scalar(not_implemented, dest)?; + } "GetProcessHeap" if this.frame_in_std() => { let [] = this.check_shim_sig( shim_sig!(extern "system" fn() -> winapi::HANDLE), @@ -1215,24 +1225,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Pretend these does not exist / nothing happened, by returning zero. this.write_null(dest)?; } - "GetConsoleMode" if this.frame_in_std() => { - let [console, mode] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE, *_) -> winapi::BOOL), - (link_name, abi, args), - )?; - this.read_target_isize(console)?; - this.deref_pointer_as(mode, this.machine.layouts.u32)?; - // Indicate an error. - this.write_null(dest)?; - } - "GetFileType" if this.frame_in_std() => { - let [_file] = this.check_shim_sig( - shim_sig!(extern "system" fn(winapi::HANDLE) -> u32), - (link_name, abi, args), - )?; - // Return unknown file type. - this.write_null(dest)?; - } "AddVectoredExceptionHandler" if this.frame_in_std() => { let [_first, _handler] = this.check_shim_sig( shim_sig!(extern "system" fn(u32, *_) -> *_), diff --git a/tests/run-make/raw-dylib-cross-compilation/lib.rs b/tests/run-make/raw-dylib-cross-compilation/lib.rs deleted file mode 100644 index 8d53ac04485eb..0000000000000 --- a/tests/run-make/raw-dylib-cross-compilation/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![feature(no_core, lang_items)] -#![no_std] -#![no_core] -#![crate_type = "lib"] - -// This is needed because of #![no_core]: -#[lang = "pointee_sized"] -trait PointeeSized {} -#[lang = "meta_sized"] -trait MetaSized: PointeeSized {} -#[lang = "sized"] -trait Sized: MetaSized {} - -#[link(name = "extern_1", kind = "raw-dylib")] -extern "C" { - fn extern_fn(); -} - -pub fn extern_fn_caller() { - unsafe { - extern_fn(); - } -} diff --git a/tests/run-make/raw-dylib-cross-compilation/rmake.rs b/tests/run-make/raw-dylib-cross-compilation/rmake.rs deleted file mode 100644 index 3eb55546314df..0000000000000 --- a/tests/run-make/raw-dylib-cross-compilation/rmake.rs +++ /dev/null @@ -1,41 +0,0 @@ -// When cross-compiling using `raw-dylib`, rustc would try to fetch some -// very specific `dlltool` to complete the cross-compilation (such as `i686-w64-mingw32-dlltool`) -// when Windows only calls it `dlltool`. This test performs some cross-compilation in a -// way that previously failed due to this bug, and checks that it succeeds. -// See https://github.com/rust-lang/rust/pull/108355 - -//@ ignore-i686-pc-windows-gnu -// Reason: dlltool on this distribution is unable to produce x64 binaries -//@ needs-dlltool -// Reason: this is the utility being checked by this test - -use run_make_support::{llvm_objdump, rust_lib_name, rustc}; - -fn main() { - // Build as x86 and make sure that we have x86 objects only. - rustc() - .crate_type("lib") - .crate_name("i686_raw_dylib_test") - .target("i686-pc-windows-gnu") - .input("lib.rs") - .run(); - llvm_objdump() - .arg("-a") - .input(rust_lib_name("i686_raw_dylib_test")) - .run() - .assert_stdout_contains("file format coff-i386") - .assert_stdout_not_contains("file format coff-x86-64"); - // Build as x64 and make sure that we have x64 objects only. - rustc() - .crate_type("lib") - .crate_name("x64_raw_dylib_test") - .target("x86_64-pc-windows-gnu") - .input("lib.rs") - .run(); - llvm_objdump() - .arg("-a") - .input(rust_lib_name("x64_raw_dylib_test")) - .run() - .assert_stdout_not_contains("file format coff-i386") - .assert_stdout_contains("file format coff-x86-64"); -} diff --git a/tests/run-make/raw-dylib-custom-dlltool/lib.rs b/tests/run-make/raw-dylib-custom-dlltool/lib.rs deleted file mode 100644 index db1d645050388..0000000000000 --- a/tests/run-make/raw-dylib-custom-dlltool/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[link(name = "extern_1", kind = "raw-dylib")] -extern "C" { - fn extern_fn_1(); -} - -pub fn library_function() { - unsafe { - extern_fn_1(); - } -} diff --git a/tests/run-make/raw-dylib-custom-dlltool/output.txt b/tests/run-make/raw-dylib-custom-dlltool/output.txt deleted file mode 100644 index 6dd9466d26ddc..0000000000000 --- a/tests/run-make/raw-dylib-custom-dlltool/output.txt +++ /dev/null @@ -1 +0,0 @@ -Called dlltool via script.cmd diff --git a/tests/run-make/raw-dylib-custom-dlltool/rmake.rs b/tests/run-make/raw-dylib-custom-dlltool/rmake.rs deleted file mode 100644 index 86b952f0b4520..0000000000000 --- a/tests/run-make/raw-dylib-custom-dlltool/rmake.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Instead of using the default dlltool, the rust compiler can also accept a custom -// command file with the -C dlltool flag. This test uses it to compile some rust code -// with the raw_dylib Windows-exclusive feature, and checks that the output contains -// the string passed from the custom dlltool, confirming that the default dlltool was -// successfully overridden. -// See https://github.com/rust-lang/rust/pull/109677 - -//@ only-windows -//@ only-gnu -//@ needs-dlltool -// Reason: this test specifically checks the custom dlltool feature, only -// available on Windows-gnu. - -use run_make_support::{diff, rustc}; - -fn main() { - let out = rustc() - .crate_type("lib") - .crate_name("raw_dylib_test") - .input("lib.rs") - .arg("-Cdlltool=script.cmd") - .run(); - diff().expected_file("output.txt").actual_file("actual.txt").normalize(r#"\r"#, "").run(); -} diff --git a/tests/run-make/raw-dylib-custom-dlltool/script.cmd b/tests/run-make/raw-dylib-custom-dlltool/script.cmd deleted file mode 100644 index c88878151af48..0000000000000 --- a/tests/run-make/raw-dylib-custom-dlltool/script.cmd +++ /dev/null @@ -1,2 +0,0 @@ -echo Called dlltool via script.cmd> %~dp0\actual.txt -dlltool.exe %* diff --git a/tests/run-make/raw-dylib-whitespace/main.rs b/tests/run-make/raw-dylib-whitespace/main.rs deleted file mode 100644 index 023c3570c3262..0000000000000 --- a/tests/run-make/raw-dylib-whitespace/main.rs +++ /dev/null @@ -1,18 +0,0 @@ -type BOOL = i32; - -#[cfg_attr( - target_arch = "x86", - link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated") -)] -#[cfg_attr(not(target_arch = "x86"), link(name = "bcryptprimitives", kind = "raw-dylib"))] -extern "system" { - fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; -} - -fn main() { - let mut num: u8 = 0; - unsafe { - ProcessPrng(&mut num, 1); - } - println!("{num}"); -} diff --git a/tests/run-make/raw-dylib-whitespace/rmake.rs b/tests/run-make/raw-dylib-whitespace/rmake.rs deleted file mode 100644 index 4f90eeb055142..0000000000000 --- a/tests/run-make/raw-dylib-whitespace/rmake.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Ensure that raw-dylib still works if the output directory contains spaces. - -//@ only-windows-gnu -//@ ignore-cross-compile -//@ needs-dlltool -// Reason: this test specifically checks the dlltool feature, -// which is only used on windows-gnu. - -use run_make_support::{rfs, rustc}; - -fn main() { - let out_dir = std::path::absolute("path with spaces").unwrap(); - rfs::create_dir_all(&out_dir); - rustc().crate_type("bin").input("main.rs").out_dir(&out_dir).env("TMP", &out_dir).run(); -} diff --git a/tests/rustdoc-html/impl/must_implement_one_of.rs b/tests/rustdoc-html/impl/must_implement_one_of.rs index ab965bab35667..2d905df0a4566 100644 --- a/tests/rustdoc-html/impl/must_implement_one_of.rs +++ b/tests/rustdoc-html/impl/must_implement_one_of.rs @@ -3,7 +3,7 @@ #[rustc_must_implement_one_of(a, b)] //@ matches c/trait.Trait.html '//*[@class="stab must_implement"]' \ -// 'At least one of the `a`, `b` methods is required.$' +// 'At least one of the a, b methods is required.$' pub trait Trait { fn a() {} fn b() {} diff --git a/tests/rustdoc-html/trait-rustc_must_implement_one_of.rs b/tests/rustdoc-html/trait-rustc_must_implement_one_of.rs new file mode 100644 index 0000000000000..57accd9fa0791 --- /dev/null +++ b/tests/rustdoc-html/trait-rustc_must_implement_one_of.rs @@ -0,0 +1,50 @@ +// This test ensures a few things around the `rustc_must_implement_one_of` attribute: +// 1. The listed methods are links +// 2. It gets a heading in the sidebar even if there is no "required methods" +// Regression test for . + +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#![crate_name = "foo"] + +//@ has 'foo/trait.Read.html' +//@ has - '//*[@class="stab must_implement"]/a[@href="#method.read"]' 'read' +//@ has - '//*[@class="stab must_implement"]/a[@href="#method.read_buf"]' 'read_buf' + +// There should be 3 headings in the sidebar: Required/Provided methods and "Implementors". +//@ count - '//*[@id="rustdoc-toc"]/h3' 3 +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#required-methods"]' 'Required Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#provided-methods"]' 'Provided Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#implementors"]' 'Implementors' + +#[rustc_must_implement_one_of(read_buf, read)] +pub trait Read { + fn read_buf(&self) {} + fn read(&self) {} +} + +//@ has 'foo/trait.Tadam.html' +// There should still be 3 headings in the sidebar: Required/Provided methods and "Implementors". +//@ count - '//*[@id="rustdoc-toc"]/h3' 3 +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#required-methods"]' 'Required Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#provided-methods"]' 'Provided Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#implementors"]' 'Implementors' + +#[rustc_must_implement_one_of(read_buf, read)] +pub trait Tadam { + fn read_buf(&self) {} + fn read(&self) {} + fn foo(&self) {} +} + +//@ has 'foo/trait.Other.html' +// There should be 2 headings in the sidebar: Provided methods and "Implementors". +//@ count - '//*[@id="rustdoc-toc"]/h3' 2 +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#provided-methods"]' 'Provided Methods' +//@ has - '//*[@id="rustdoc-toc"]/h3/a[@href="#implementors"]' 'Implementors' + +pub trait Other { + fn read_buf(&self) {} + fn read(&self) {} +} diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs deleted file mode 100644 index 369c2dd9c4877..0000000000000 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Test the `rustc::span_use_eq_ctxt` internal lint -//@ compile-flags: -Z unstable-options - -#![feature(rustc_private)] -#![deny(rustc::span_use_eq_ctxt)] -#![crate_type = "lib"] - -extern crate rustc_span; -use rustc_span::Span; - -pub fn f(s: Span, t: Span) -> bool { - s.ctxt() == t.ctxt() //~ ERROR use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` -} diff --git a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr b/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr deleted file mode 100644 index b52df0368b16d..0000000000000 --- a/tests/ui-fulldeps/internal-lints/span_use_eq_ctxt.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` - --> $DIR/span_use_eq_ctxt.rs:12:5 - | -LL | s.ctxt() == t.ctxt() - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/span_use_eq_ctxt.rs:5:9 - | -LL | #![deny(rustc::span_use_eq_ctxt)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.rs b/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.rs new file mode 100644 index 0000000000000..c98afb8474448 --- /dev/null +++ b/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +#![feature(test_binder_constraints, non_lifetime_binders)] +#![expect(incomplete_features)] + +core::test_binder_constraints! { + impl<'a> { + forall<'b> { + //~^ ERROR forall evaluated to false + 'b: 'a + } + } +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.stderr b/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.stderr new file mode 100644 index 0000000000000..995363d521621 --- /dev/null +++ b/tests/ui/assumptions_on_binders/test-forall-evaluates-to-false.stderr @@ -0,0 +1,8 @@ +error: forall evaluated to false + --> $DIR/test-forall-evaluates-to-false.rs:8:9 + | +LL | forall<'b> { + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/assumptions_on_binders/test-top-level-false-constraint.rs b/tests/ui/assumptions_on_binders/test-top-level-false-constraint.rs new file mode 100644 index 0000000000000..cdd7eb7a321bc --- /dev/null +++ b/tests/ui/assumptions_on_binders/test-top-level-false-constraint.rs @@ -0,0 +1,11 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +#![feature(test_binder_constraints, non_lifetime_binders)] +#![expect(incomplete_features)] + +core::test_binder_constraints! { + impl { or {} } + //~^ ERROR top level false constraints are not supported +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/test-top-level-false-constraint.stderr b/tests/ui/assumptions_on_binders/test-top-level-false-constraint.stderr new file mode 100644 index 0000000000000..fb5380879fe3d --- /dev/null +++ b/tests/ui/assumptions_on_binders/test-top-level-false-constraint.stderr @@ -0,0 +1,8 @@ +error: top level false constraints are not supported + --> $DIR/test-top-level-false-constraint.rs:7:10 + | +LL | impl { or {} } + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/coherence/overlap-check-gat-alias-hang-151503.rs b/tests/ui/coherence/overlap-check-gat-alias-hang-151503.rs new file mode 100644 index 0000000000000..21d6342dedb72 --- /dev/null +++ b/tests/ui/coherence/overlap-check-gat-alias-hang-151503.rs @@ -0,0 +1,29 @@ +//! Regression test for . +//! +//! The overlap check for the two `MyFrom` impls used to hang. + +trait HasAlias {} +struct Dummy; +trait DummyTrait { + type DummyType; +} +impl DummyTrait for Dummy { + type DummyType = T; +} +type AliasOf = ::DummyType; +struct Matrix(T, S); +type OMatrix = Matrix>; + +trait SimdValue { + type Element; +} +impl SimdValue for OMatrix { + type Element = OMatrix; +} +trait MyFrom {} +impl MyFrom for T {} +impl MyFrom for OMatrix {} +//~^ ERROR conflicting implementations of trait `MyFrom>` for type `Matrix<_, _>` +impl HasAlias for Matrix {} + +fn main() {} diff --git a/tests/ui/coherence/overlap-check-gat-alias-hang-151503.stderr b/tests/ui/coherence/overlap-check-gat-alias-hang-151503.stderr new file mode 100644 index 0000000000000..a20b12334f2fc --- /dev/null +++ b/tests/ui/coherence/overlap-check-gat-alias-hang-151503.stderr @@ -0,0 +1,11 @@ +error[E0119]: conflicting implementations of trait `MyFrom>` for type `Matrix<_, _>` + --> $DIR/overlap-check-gat-alias-hang-151503.rs:25:1 + | +LL | impl MyFrom for T {} + | ----------------------- first implementation here +LL | impl MyFrom for OMatrix {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Matrix<_, _>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/diagnostic_namespace/on_unknown/auxiliary/foreign.rs b/tests/ui/diagnostic_namespace/on_unknown/auxiliary/foreign.rs new file mode 100644 index 0000000000000..c9269fcb84ca2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/auxiliary/foreign.rs @@ -0,0 +1,12 @@ +#![feature(diagnostic_on_unknown)] + +mod a { + #[diagnostic::on_unknown( + message = "this={this}, unresolved={unresolved}", + )] + pub mod b {} +} + +pub mod c { + pub use super::a::b as d; +} diff --git a/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.rs b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.rs new file mode 100644 index 0000000000000..d8bf14ccc54c3 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.rs @@ -0,0 +1,20 @@ +#![feature(diagnostic_on_unknown)] + +mod a { + #[diagnostic::on_unknown( + message = "this={this}, unresolved={unresolved}", + )] + pub mod b {} +} + +mod c { + pub use super::a::b as d; +} + +use c::d::x; //~ ERROR this=b, unresolved=x + +fn main() { + let _ = c::d::X(); //~ ERROR this=b, unresolved=X + + let _: c::d::X; //~ ERROR this=b, unresolved=X +} diff --git a/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.stderr b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.stderr new file mode 100644 index 0000000000000..a269acdf42b17 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name.stderr @@ -0,0 +1,30 @@ +error[E0432]: this=b, unresolved=x + --> $DIR/expressions_and_paths_correct_mod_name.rs:14:5 + | +LL | use c::d::x; + | ^^^^^^- + | | + | no `x` in `a::b` + | + = note: unresolved import `c::d::x` + +error[E0425]: this=b, unresolved=X + --> $DIR/expressions_and_paths_correct_mod_name.rs:17:19 + | +LL | let _ = c::d::X(); + | ^ not found in `c::d` + | + = note: cannot find function, tuple struct or tuple variant `X` in module `c::d` + +error[E0425]: this=b, unresolved=X + --> $DIR/expressions_and_paths_correct_mod_name.rs:19:18 + | +LL | let _: c::d::X; + | ^ not found in `c::d` + | + = note: cannot find type `X` in module `c::d` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0425, E0432. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.rs b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.rs new file mode 100644 index 0000000000000..6cb8872984b36 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.rs @@ -0,0 +1,11 @@ +//@ aux-crate:foreign=foreign.rs + +extern crate foreign; + +use foreign::c::d::x; //~ ERROR this=b, unresolved=x + +fn main() { + let _ = foreign::c::d::X(); //~ ERROR this=b, unresolved=X + + let _: foreign::c::d::X; //~ ERROR this=b, unresolved=X +} diff --git a/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.stderr b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.stderr new file mode 100644 index 0000000000000..ee1765828718f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/expressions_and_paths_correct_mod_name_foreign.stderr @@ -0,0 +1,30 @@ +error[E0432]: this=b, unresolved=x + --> $DIR/expressions_and_paths_correct_mod_name_foreign.rs:5:5 + | +LL | use foreign::c::d::x; + | ^^^^^^^^^^^^^^^- + | | + | no `x` in `a::b` + | + = note: unresolved import `foreign::c::d::x` + +error[E0425]: this=b, unresolved=X + --> $DIR/expressions_and_paths_correct_mod_name_foreign.rs:8:28 + | +LL | let _ = foreign::c::d::X(); + | ^ not found in `foreign::c::d` + | + = note: cannot find function, tuple struct or tuple variant `X` in module `foreign::c::d` + +error[E0425]: this=b, unresolved=X + --> $DIR/expressions_and_paths_correct_mod_name_foreign.rs:10:27 + | +LL | let _: foreign::c::d::X; + | ^ not found in `foreign::c::d` + | + = note: cannot find type `X` in module `foreign::c::d` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0425, E0432. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/feature-gates/feature-gate-move_expr.stderr b/tests/ui/feature-gates/feature-gate-move_expr.stderr index 8b1da2c06893d..41a2ded842207 100644 --- a/tests/ui/feature-gates/feature-gate-move_expr.stderr +++ b/tests/ui/feature-gates/feature-gate-move_expr.stderr @@ -2,7 +2,7 @@ error[E0658]: `move(expr)` syntax is experimental --> $DIR/feature-gate-move_expr.rs:2:16 | LL | let _ = || move(2); - | ^^^^ + | ^^^^^^^ | = note: see issue #155050 for more information = help: add `#![feature(move_expr)]` to the crate attributes to enable diff --git a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs deleted file mode 100644 index ae326193550bc..0000000000000 --- a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Tests that dlltool failing to generate an import library will raise an error. - -//@ needs-dlltool -//@ compile-flags: --crate-type lib --emit link -//@ normalize-stderr: "[^ ']*/dlltool.exe" -> "$$DLLTOOL" -//@ normalize-stderr: "[^ ]*/foo.dll_imports.def" -> "$$DEF_FILE" -//@ normalize-stderr: "[^ ]*/foo.dll_imports.lib" -> "$$LIB_FILE" -//@ normalize-stderr: "-m [^ ]*" -> "$$TARGET_MACHINE" -//@ normalize-stderr: "-f [^ ]*" -> "$$ASM_FLAGS" -#[link(name = "foo", kind = "raw-dylib")] -extern "C" { - // `@1` is an invalid name to export, as it usually indicates that something - // is being exported via ordinal. - #[link_name = "@1"] - fn f(x: i32); -} - -pub fn lib_main() { - unsafe { f(42); } -} - -//~? ERROR dlltool could not create import library with diff --git a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr deleted file mode 100644 index b7279f23e6676..0000000000000 --- a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: dlltool could not create import library with $DLLTOOL -d $DEF_FILE -D foo.dll -l $LIB_FILE $TARGET_MACHINE $ASM_FLAGS --no-leading-underscore --temp-prefix foo.dll: - - $DLLTOOL: Syntax error in def file $DEF_FILE:1␍ - - -error: aborting due to 1 previous error - diff --git a/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs b/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs deleted file mode 100644 index f5f92db3b501a..0000000000000 --- a/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Tests that failing to run dlltool will raise an error. - -//@ needs-dlltool -//@ compile-flags: --crate-type lib --emit link -Cdlltool=does_not_exist.exe -#[link(name = "foo", kind = "raw-dylib")] -extern "C" { - fn f(x: i32); -} - -pub fn lib_main() { - unsafe { f(42); } -} - -//~? ERROR error calling dlltool 'does_not_exist.exe': program not found diff --git a/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.stderr b/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.stderr deleted file mode 100644 index 311d8c61589a4..0000000000000 --- a/tests/ui/linkage-attr/raw-dylib/windows/invalid-dlltool.stderr +++ /dev/null @@ -1,4 +0,0 @@ -error: error calling dlltool 'does_not_exist.exe': program not found - -error: aborting due to 1 previous error - diff --git a/tests/ui/move-expr/left-operand-of-dot-call-index-try.rs b/tests/ui/move-expr/left-operand-of-dot-call-index-try.rs new file mode 100644 index 0000000000000..700101a8a27f2 --- /dev/null +++ b/tests/ui/move-expr/left-operand-of-dot-call-index-try.rs @@ -0,0 +1,21 @@ +// Ensure that move exprs can be the left operand of dot, call, index & try ops. We once used to +// incorrectly parse move exprs as prefix exprs instead of bottom ones which used to prevent that. + +//@ check-pass +//@ edition: 2018.. +#![feature(move_expr)] + +fn main() { + let _ = async || { move(async {}).await }; // Await(Move(_)) + + let _ = || -> Result<(), ()> { + move(((),)).0; // Field(Move(_), ..) + move(Err(()))?; // Try(Move(_)) + move(drop)(()); // Call(Move(_), ..) + move([0])[0]; // Index(Move(_), ..) + + // Previously, we would parse this as Field(Return(Move(_)), ..) (`(return move(…)).0`) + // instead of Return(Field(Move(_), ..)) and thus fail with "no field `0` on type `!`". + return move((Ok(()),)).0; // OK! + }; +} diff --git a/tests/ui/parser/issues/issue-93282.rs b/tests/ui/parser/issues/issue-93282.rs index 274245f1a465f..5b96969f5b971 100644 --- a/tests/ui/parser/issues/issue-93282.rs +++ b/tests/ui/parser/issues/issue-93282.rs @@ -1,7 +1,10 @@ fn main() { + //~^ HELP consider introducing lifetime `'a` here f<'a,> - //~^ ERROR expected - //~| ERROR expected + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + //~| ERROR use of undeclared lifetime name `'a` + //~| ERROR cannot find value `f` in this scope } fn bar(a: usize, b: usize) -> usize { @@ -12,5 +15,6 @@ fn foo() { let x = 1; bar('y, x); //~^ ERROR expected + //~| HELP add `'` to close the char literal //~| ERROR mismatched types } diff --git a/tests/ui/parser/issues/issue-93282.stderr b/tests/ui/parser/issues/issue-93282.stderr index c6140bb821e48..1daacff52f066 100644 --- a/tests/ui/parser/issues/issue-93282.stderr +++ b/tests/ui/parser/issues/issue-93282.stderr @@ -1,27 +1,16 @@ -error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/issue-93282.rs:2:9 - | -LL | f<'a,> - | ^ expected `while`, `for`, `loop` or `{` after a label - | -help: add `'` to close the char literal - | -LL | f<'a',> - | + - -error: expected one of `.`, `:`, `;`, `?`, `for`, `loop`, `while`, `}`, or an operator, found `,` - --> $DIR/issue-93282.rs:2:9 +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/issue-93282.rs:3:6 | LL | f<'a,> - | ^ expected one of 9 possible tokens + | ^ | -help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments +help: add `::` | LL | f::<'a,> | ++ error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/issue-93282.rs:13:11 + --> $DIR/issue-93282.rs:16:11 | LL | bar('y, x); | ^ expected `while`, `for`, `loop` or `{` after a label @@ -31,8 +20,25 @@ help: add `'` to close the char literal LL | bar('y', x); | + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/issue-93282.rs:3:7 + | +LL | f<'a,> + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn main<'a>() { + | ++++ + +error[E0425]: cannot find value `f` in this scope + --> $DIR/issue-93282.rs:3:5 + | +LL | f<'a,> + | ^ not found in this scope + error[E0308]: mismatched types - --> $DIR/issue-93282.rs:13:9 + --> $DIR/issue-93282.rs:16:9 | LL | bar('y, x); | --- ^^ expected `usize`, found `char` @@ -40,11 +46,12 @@ LL | bar('y, x); | arguments to this function are incorrect | note: function defined here - --> $DIR/issue-93282.rs:7:4 + --> $DIR/issue-93282.rs:10:4 | LL | fn bar(a: usize, b: usize) -> usize { | ^^^ -------- -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0261, E0308, E0425. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime-macro.rs b/tests/ui/parser/recover/missing-turbofish-lifetime-macro.rs new file mode 100644 index 0000000000000..366fc8ddfd982 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime-macro.rs @@ -0,0 +1,16 @@ +macro_rules! m { + ($e:expr) => { "expr" }; + (a < $l:lifetime >) => { "lifetime" }; +} + +fn test_macro() { + // Ensure turbofish recovery is disabled during macro matching to avoid spurious diagnostics. + // The second arm does NOT match; the expr-fragment error is a hard error. + let _ = m!(a < 'x >); + //~^ ERROR comparison operators cannot be chained + //~| HELP use `::<...>` instead of `<...>` + //~| HELP or use `(...)` + //~| ERROR expected `while`, `for`, `loop` or `{` after a label +} + +fn main() {} diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime-macro.stderr b/tests/ui/parser/recover/missing-turbofish-lifetime-macro.stderr new file mode 100644 index 0000000000000..a05d7e668a521 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime-macro.stderr @@ -0,0 +1,20 @@ +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/missing-turbofish-lifetime-macro.rs:9:23 + | +LL | let _ = m!(a < 'x >); + | ^ expected `while`, `for`, `loop` or `{` after a label + +error: comparison operators cannot be chained + --> $DIR/missing-turbofish-lifetime-macro.rs:9:18 + | +LL | ($e:expr) => { "expr" }; + | ------- while parsing argument for this `expr` macro fragment +... +LL | let _ = m!(a < 'x >); + | ^ ^ + | + = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + = help: or use `(...)` if you meant to specify fn arguments + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime.fixed b/tests/ui/parser/recover/missing-turbofish-lifetime.fixed new file mode 100644 index 0000000000000..f5d8e2e63e6d5 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime.fixed @@ -0,0 +1,87 @@ +//! Regression test for +//! +//! Test that omitting the turbofish when passing a lifetime to an expression +//! emits a targeted suggestion rather than a cascading syntax error. + +#![allow(dead_code)] + +//@ run-rustfix + +struct Struct<'a> { + string: &'a str, +} + +fn struct_with_reserved_lifetime() { + let _ = Struct::<'_> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +fn struct_with_named_lifetime<'a>() { + let _ = Struct::<'a> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +fn struct_with_multichar_lifetime<'abc>() { + let _ = Struct::<'abc> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +struct TupleStruct<'a>(&'a str); + +fn tuple_struct_with_reserved_lifetime() { + let _ = TupleStruct::<'_>(""); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` +} + +struct Wrapper<'a>(&'a str); +impl<'a> Wrapper<'a> { fn new(s: &'a str) -> Self { Self(s) } } + +struct Struct2<'a> { x: i32, _p: std::marker::PhantomData<&'a ()> } +impl<'a> Struct2<'a> { fn method(&self) -> Option<()> { Some(()) } } + +fn f<'a: 'a>() {} + +struct StructT<'a, T> { a: &'a str, b: T } +struct Struct3<'a, 'b> { a: &'a str, b: &'b str } + +fn chaining_cases<'a, 'b, T>() -> Option<()> { + let _ = Wrapper::<'a>::new("hi"); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + let _ = Struct2::<'a> { x: 1, _p: std::marker::PhantomData }.method(); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + let _ = Struct2::<'a> { x: 1, _p: std::marker::PhantomData }.method()?; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + f::<'_>(); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + // struct with two lifetimes + let _ = Struct3::<'a, 'b> { a: "hi", b: "hi" }; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + // struct with lifetime and type + let _ = StructT::<'a, &str> { a: "hi", b: "hi" }; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + Some(()) +} + +fn main() {} diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime.rs b/tests/ui/parser/recover/missing-turbofish-lifetime.rs new file mode 100644 index 0000000000000..1bc9e97878739 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime.rs @@ -0,0 +1,87 @@ +//! Regression test for +//! +//! Test that omitting the turbofish when passing a lifetime to an expression +//! emits a targeted suggestion rather than a cascading syntax error. + +#![allow(dead_code)] + +//@ run-rustfix + +struct Struct<'a> { + string: &'a str, +} + +fn struct_with_reserved_lifetime() { + let _ = Struct<'_> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +fn struct_with_named_lifetime<'a>() { + let _ = Struct<'a> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +fn struct_with_multichar_lifetime<'abc>() { + let _ = Struct<'abc> { + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + string: "", + }; +} + +struct TupleStruct<'a>(&'a str); + +fn tuple_struct_with_reserved_lifetime() { + let _ = TupleStruct<'_>(""); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` +} + +struct Wrapper<'a>(&'a str); +impl<'a> Wrapper<'a> { fn new(s: &'a str) -> Self { Self(s) } } + +struct Struct2<'a> { x: i32, _p: std::marker::PhantomData<&'a ()> } +impl<'a> Struct2<'a> { fn method(&self) -> Option<()> { Some(()) } } + +fn f<'a: 'a>() {} + +struct StructT<'a, T> { a: &'a str, b: T } +struct Struct3<'a, 'b> { a: &'a str, b: &'b str } + +fn chaining_cases<'a, 'b, T>() -> Option<()> { + let _ = Wrapper<'a>::new("hi"); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + let _ = Struct2<'a> { x: 1, _p: std::marker::PhantomData }.method(); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + let _ = Struct2<'a> { x: 1, _p: std::marker::PhantomData }.method()?; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + f<'_>(); + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + // struct with two lifetimes + let _ = Struct3<'a, 'b> { a: "hi", b: "hi" }; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + // struct with lifetime and type + let _ = StructT<'a, &str> { a: "hi", b: "hi" }; + //~^ ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` + + Some(()) +} + +fn main() {} diff --git a/tests/ui/parser/recover/missing-turbofish-lifetime.stderr b/tests/ui/parser/recover/missing-turbofish-lifetime.stderr new file mode 100644 index 0000000000000..faea5463bdfd4 --- /dev/null +++ b/tests/ui/parser/recover/missing-turbofish-lifetime.stderr @@ -0,0 +1,112 @@ +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:15:19 + | +LL | let _ = Struct<'_> { + | ^ + | +help: add `::` + | +LL | let _ = Struct::<'_> { + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:23:19 + | +LL | let _ = Struct<'a> { + | ^ + | +help: add `::` + | +LL | let _ = Struct::<'a> { + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:31:19 + | +LL | let _ = Struct<'abc> { + | ^ + | +help: add `::` + | +LL | let _ = Struct::<'abc> { + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:41:24 + | +LL | let _ = TupleStruct<'_>(""); + | ^ + | +help: add `::` + | +LL | let _ = TupleStruct::<'_>(""); + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:58:20 + | +LL | let _ = Wrapper<'a>::new("hi"); + | ^ + | +help: add `::` + | +LL | let _ = Wrapper::<'a>::new("hi"); + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:62:20 + | +LL | let _ = Struct2<'a> { x: 1, _p: std::marker::PhantomData }.method(); + | ^ + | +help: add `::` + | +LL | let _ = Struct2::<'a> { x: 1, _p: std::marker::PhantomData }.method(); + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:66:20 + | +LL | let _ = Struct2<'a> { x: 1, _p: std::marker::PhantomData }.method()?; + | ^ + | +help: add `::` + | +LL | let _ = Struct2::<'a> { x: 1, _p: std::marker::PhantomData }.method()?; + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:70:6 + | +LL | f<'_>(); + | ^ + | +help: add `::` + | +LL | f::<'_>(); + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:75:20 + | +LL | let _ = Struct3<'a, 'b> { a: "hi", b: "hi" }; + | ^ + | +help: add `::` + | +LL | let _ = Struct3::<'a, 'b> { a: "hi", b: "hi" }; + | ++ + +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/missing-turbofish-lifetime.rs:80:20 + | +LL | let _ = StructT<'a, &str> { a: "hi", b: "hi" }; + | ^ + | +help: add `::` + | +LL | let _ = StructT::<'a, &str> { a: "hi", b: "hi" }; + | ++ + +error: aborting due to 10 previous errors + diff --git a/tests/ui/parser/require-parens-for-chained-comparison.rs b/tests/ui/parser/require-parens-for-chained-comparison.rs index 6152fff6c0365..4162e9f27f36d 100644 --- a/tests/ui/parser/require-parens-for-chained-comparison.rs +++ b/tests/ui/parser/require-parens-for-chained-comparison.rs @@ -20,18 +20,14 @@ fn main() { //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments let _ = f<'_, i8>(); - //~^ ERROR expected one of - //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments - //~| ERROR expected - //~| HELP add `'` to close the char literal - //~| ERROR labels cannot use keyword names + //~^ ERROR cannot find function `f` in this scope + //~| ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` f<'_>(); - //~^ ERROR comparison operators cannot be chained - //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments - //~| ERROR expected - //~| HELP add `'` to close the char literal - //~| ERROR labels cannot use keyword names + //~^ ERROR cannot find function `f` in this scope + //~| ERROR use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + //~| HELP add `::` let _ = f; //~^ ERROR comparison operators cannot be chained diff --git a/tests/ui/parser/require-parens-for-chained-comparison.stderr b/tests/ui/parser/require-parens-for-chained-comparison.stderr index 9edfae3625027..50f66927db118 100644 --- a/tests/ui/parser/require-parens-for-chained-comparison.stderr +++ b/tests/ui/parser/require-parens-for-chained-comparison.stderr @@ -53,64 +53,30 @@ help: use `::<...>` instead of `<...>` to specify lifetime, type, or const argum LL | let _ = f::(); | ++ -error: labels cannot use keyword names - --> $DIR/require-parens-for-chained-comparison.rs:22:15 +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/require-parens-for-chained-comparison.rs:22:14 | LL | let _ = f<'_, i8>(); - | ^^ - -error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/require-parens-for-chained-comparison.rs:22:17 - | -LL | let _ = f<'_, i8>(); - | ^ expected `while`, `for`, `loop` or `{` after a label - | -help: add `'` to close the char literal + | ^ | -LL | let _ = f<'_', i8>(); - | + - -error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, or an operator, found `,` - --> $DIR/require-parens-for-chained-comparison.rs:22:17 - | -LL | let _ = f<'_, i8>(); - | ^ expected one of 9 possible tokens - | -help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments +help: add `::` | LL | let _ = f::<'_, i8>(); | ++ -error: labels cannot use keyword names - --> $DIR/require-parens-for-chained-comparison.rs:29:7 - | -LL | f<'_>(); - | ^^ - -error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/require-parens-for-chained-comparison.rs:29:9 +error: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + --> $DIR/require-parens-for-chained-comparison.rs:27:6 | LL | f<'_>(); - | ^ expected `while`, `for`, `loop` or `{` after a label + | ^ | -help: add `'` to close the char literal - | -LL | f<'_'>(); - | + - -error: comparison operators cannot be chained - --> $DIR/require-parens-for-chained-comparison.rs:29:6 - | -LL | f<'_>(); - | ^ ^ - | -help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments +help: add `::` | LL | f::<'_>(); | ++ error: comparison operators cannot be chained - --> $DIR/require-parens-for-chained-comparison.rs:36:14 + --> $DIR/require-parens-for-chained-comparison.rs:32:14 | LL | let _ = f; | ^ ^ @@ -118,5 +84,18 @@ LL | let _ = f; = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments = help: or use `(...)` if you meant to specify fn arguments -error: aborting due to 12 previous errors +error[E0425]: cannot find function `f` in this scope + --> $DIR/require-parens-for-chained-comparison.rs:22:13 + | +LL | let _ = f<'_, i8>(); + | ^ not found in this scope + +error[E0425]: cannot find function `f` in this scope + --> $DIR/require-parens-for-chained-comparison.rs:27:5 + | +LL | f<'_>(); + | ^ not found in this scope + +error: aborting due to 10 previous errors +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.rs b/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.rs new file mode 100644 index 0000000000000..0d45d1ce9b1c4 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Znext-solver + +// Regression test for #155977: the new solver used to ICE while normalizing +// a higher-ranked where-clause in the param env. +// Fixed by eager param-env normalization in #156976. + +trait Trait { + type Output; +} + +fn walk2() +//~^ ERROR the trait bound +where + for<'a> F: 'a, + for<'a> >::Output: 'a, +{ +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.stderr b/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.stderr new file mode 100644 index 0000000000000..1889e3be86aba --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/higher-ranked-param-env-normalization-155977.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `for<'a> F: Trait<&'a u32>` is not satisfied + --> $DIR/higher-ranked-param-env-normalization-155977.rs:11:1 + | +LL | / fn walk2() +LL | | +LL | | where +LL | | for<'a> F: 'a, +LL | | for<'a> >::Output: 'a, + | |______________________________________________^ the trait `for<'a> Trait<&'a u32>` is not implemented for `F` + | +help: consider further restricting type parameter `F` with trait `Trait` + | +LL | for<'a> F: 'a + for<'a> Trait<&'a u32>, + | ++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.