diff options
338 files changed, 6140 insertions, 2425 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a07342ee96c..ff4fa1527e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,6 +128,9 @@ jobs: - name: ensure line endings are correct run: src/ci/scripts/verify-line-endings.sh if: success() && !env.SKIP_JOB + - name: ensure backported commits are in upstream branches + run: src/ci/scripts/verify-backported-commits.sh + if: success() && !env.SKIP_JOB - name: run the build run: src/ci/scripts/run-build-from-ci.sh env: @@ -499,6 +502,9 @@ jobs: - name: ensure line endings are correct run: src/ci/scripts/verify-line-endings.sh if: success() && !env.SKIP_JOB + - name: ensure backported commits are in upstream branches + run: src/ci/scripts/verify-backported-commits.sh + if: success() && !env.SKIP_JOB - name: run the build run: src/ci/scripts/run-build-from-ci.sh env: @@ -609,6 +615,9 @@ jobs: - name: ensure line endings are correct run: src/ci/scripts/verify-line-endings.sh if: success() && !env.SKIP_JOB + - name: ensure backported commits are in upstream branches + run: src/ci/scripts/verify-backported-commits.sh + if: success() && !env.SKIP_JOB - name: run the build run: src/ci/scripts/run-build-from-ci.sh env: diff --git a/.gitmodules b/.gitmodules index 60ccf9375ee..e22d5762790 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,7 +34,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/12.0-2021-07-10 + branch = rustc/13.0-2021-08-08 [submodule "src/doc/embedded-book"] path = src/doc/embedded-book url = https://github.com/rust-embedded/book.git diff --git a/.mailmap b/.mailmap index 56ac5296774..2fbb480b85e 100644 --- a/.mailmap +++ b/.mailmap @@ -101,6 +101,7 @@ Falco Hirschenberger <falco.hirschenberger@gmail.com> <hirschen@itwm.fhg.de> Felix S. Klock II <pnkfelix@pnkfx.org> Felix S Klock II <pnkfelix@pnkfx.org> Flaper Fesp <flaper87@gmail.com> Florian Wilkens <mrfloya_github@outlook.com> Florian Wilkens <floya@live.de> +Frank Steffahn <fdsteffahn@gmail.com> <frank.steffahn@stu.uni-kiel.de> Gareth Daniel Smith <garethdanielsmith@gmail.com> gareth <gareth@gareth-N56VM.(none)> Gareth Daniel Smith <garethdanielsmith@gmail.com> Gareth Smith <garethdanielsmith@gmail.com> Georges Dubus <georges.dubus@gmail.com> <georges.dubus@compiletoi.net> @@ -113,6 +114,7 @@ Hanna Kruppe <hanna.kruppe@gmail.com> <robin.kruppe@gmail.com> Heather <heather@cynede.net> <Cynede@Gentoo.org> Heather <heather@cynede.net> <Heather@cynede.net> Herman J. Radtke III <herman@hermanradtke.com> Herman J. Radtke III <hermanradtke@gmail.com> +Hirochika Matsumoto <git@hkmatsumoto.com> <matsujika@gmail.com> Ian Jackson <ijackson@chiark.greenend.org.uk> <ian.jackson@citrix.com> Ian Jackson <ijackson@chiark.greenend.org.uk> <ijackson+github@slimy.greenend.org.uk> Ian Jackson <ijackson@chiark.greenend.org.uk> <iwj@xenproject.org> diff --git a/Cargo.lock b/Cargo.lock index 14cb1f1641b..9e0624c57ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" +checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" dependencies = [ "compiler_builtins", "gimli", @@ -1418,9 +1418,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.23.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" +checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -2141,9 +2141,13 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] [[package]] name = "memmap2" @@ -2293,24 +2297,16 @@ dependencies = [ [[package]] name = "object" -version = "0.22.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +checksum = "ee2766204889d09937d00bfbb7fec56bb2a199e2ade963cab19185d8a6104c7c" dependencies = [ "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "object" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bc1d42047cf336f0f939c99e97183cf31551bf0f2865a2ec9c8d91fd4ffb5e" -dependencies = [ "crc32fast", "indexmap", "memchr", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", ] [[package]] @@ -3647,7 +3643,7 @@ dependencies = [ "itertools 0.9.0", "jobserver", "libc", - "object 0.25.2", + "object", "pathdiff", "regex", "rustc_apfloat", @@ -4840,7 +4836,7 @@ dependencies = [ "hermit-abi", "libc", "miniz_oxide", - "object 0.22.0", + "object", "panic_abort", "panic_unwind", "profiler_builtins", diff --git a/RELEASES.md b/RELEASES.md index 1d9ad3160f7..2e7077ed206 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -70,6 +70,7 @@ Cargo - [The package definition in `cargo metadata` now includes the `"default_run"` field from the manifest.][cargo/9550] - [Added `cargo d` as an alias for `cargo doc`.][cargo/9680] +- [Added `{lib}` as formatting option for `cargo tree` to print the "lib_name" of packages.][cargo/9663] Rustdoc ------- @@ -146,18 +147,13 @@ Version 1.54.0 (2021-07-29) Language ----------------------- -- [You can now use macros for values in built-in attribute macros.][83366] - While a seemingly minor addition on its own, this enables a lot of - powerful functionality when combined correctly. Most notably you can - now include external documentation in your crate by writing the following. +- [You can now use macros for values in some built-in attributes.][83366] + This primarily allows you to call macros within the `#[doc]` attribute. For + example, to include external documentation in your crate, you can now write + the following: ```rust #![doc = include_str!("README.md")] ``` - You can also use this to include auto-generated modules: - ```rust - #[path = concat!(env!("OUT_DIR"), "/generated.rs")] - mod generated; - ``` - [You can now cast between unsized slice types (and types which contain unsized slices) in `const fn`.][85078] diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index d9c4647cba3..fd96858010e 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -31,21 +31,10 @@ pub(crate) fn unsized_info<'tcx>( if data_a.principal_def_id() == data_b.principal_def_id() { return old_info; } - // trait upcasting coercion - // if both of the two `principal`s are `None`, this function would have returned early above. - // and if one of the two `principal`s is `None`, typechecking would have rejected this case. - let principal_a = data_a - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - let principal_b = data_b - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - - let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( - principal_a.with_self_ty(fx.tcx, source), - principal_b.with_self_ty(fx.tcx, source), - )); + // trait upcasting coercion + let vptr_entry_idx = + fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((source, target)); if let Some(entry_idx) = vptr_entry_idx { let entry_idx = u32::try_from(entry_idx).unwrap(); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 5b4a187a1d5..791604a1827 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -296,39 +296,8 @@ unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, user: *const c_void } let (cgcx, _) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, &Handler)); - // Recover the post-substitution assembly code from LLVM for better - // diagnostics. - let mut have_source = false; - let mut buffer = String::new(); - let mut level = llvm::DiagnosticLevel::Error; - let mut loc = 0; - let mut ranges = [0; 8]; - let mut num_ranges = ranges.len() / 2; - let msg = llvm::build_string(|msg| { - buffer = llvm::build_string(|buffer| { - have_source = llvm::LLVMRustUnpackSMDiagnostic( - diag, - msg, - buffer, - &mut level, - &mut loc, - ranges.as_mut_ptr(), - &mut num_ranges, - ); - }) - .expect("non-UTF8 inline asm"); - }) - .expect("non-UTF8 SMDiagnostic"); - - let source = have_source.then(|| { - let mut spans = vec![InnerSpan::new(loc as usize, loc as usize)]; - for i in 0..num_ranges { - spans.push(InnerSpan::new(ranges[i * 2] as usize, ranges[i * 2 + 1] as usize)); - } - (buffer, spans) - }); - - report_inline_asm(cgcx, msg, level, cookie, source); + let smdiag = llvm::diagnostic::SrcMgrDiagnostic::unpack(diag); + report_inline_asm(cgcx, smdiag.message, smdiag.level, cookie, smdiag.source); } unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) { @@ -339,13 +308,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void match llvm::diagnostic::Diagnostic::unpack(info) { llvm::diagnostic::InlineAsm(inline) => { - report_inline_asm( - cgcx, - llvm::twine_to_string(inline.message), - inline.level, - inline.cookie, - None, - ); + report_inline_asm(cgcx, inline.message, inline.level, inline.cookie, inline.source); } llvm::diagnostic::Optimization(opt) => { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index cc3cbea4def..a6bdbd11899 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -157,16 +157,18 @@ pub fn compile_codegen_unit( } // Finalize code coverage by injecting the coverage map. Note, the coverage map will - // also be added to the `llvm.used` variable, created next. + // also be added to the `llvm.compiler.used` variable, created next. if cx.sess().instrument_coverage() { cx.coverageinfo_finalize(); } - // Create the llvm.used variable - // This variable has type [N x i8*] and is stored in the llvm.metadata section + // Create the llvm.used and llvm.compiler.used variables. if !cx.used_statics().borrow().is_empty() { cx.create_used_variable() } + if !cx.compiler_used_statics().borrow().is_empty() { + cx.create_compiler_used_variable() + } // Finalize debuginfo if cx.sess().opts.debuginfo != DebugInfo::None { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 3ca295f4a7e..e1baf95e1d9 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -474,7 +474,13 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { } if attrs.flags.contains(CodegenFnAttrFlags::USED) { - self.add_used_global(g); + // The semantics of #[used] in Rust only require the symbol to make it into the + // object file. It is explicitly allowed for the linker to strip the symbol if it + // is dead. As such, use llvm.compiler.used instead of llvm.used. + // Additionally, https://reviews.llvm.org/D97448 in LLVM 13 started emitting unique + // sections with SHF_GNU_RETAIN flag for llvm.used symbols, which may trigger bugs + // in some versions of the gold linker. + self.add_compiler_used_global(g); } } } @@ -484,4 +490,11 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { let cast = unsafe { llvm::LLVMConstPointerCast(global, self.type_i8p()) }; self.used_statics.borrow_mut().push(cast); } + + /// Add a global value to a list to be stored in the `llvm.compiler.used` variable, + /// an array of i8*. + fn add_compiler_used_global(&self, global: &'ll Value) { + let cast = unsafe { llvm::LLVMConstPointerCast(global, self.type_i8p()) }; + self.compiler_used_statics.borrow_mut().push(cast); + } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 5d56c93f835..35c866d48a4 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -75,6 +75,10 @@ pub struct CodegenCx<'ll, 'tcx> { /// See <https://llvm.org/docs/LangRef.html#the-llvm-used-global-variable> for details pub used_statics: RefCell<Vec<&'ll Value>>, + /// Statics that will be placed in the llvm.compiler.used variable + /// See <https://llvm.org/docs/LangRef.html#the-llvm-compiler-used-global-variable> for details + pub compiler_used_statics: RefCell<Vec<&'ll Value>>, + /// Mapping of non-scalar types to llvm types and field remapping if needed. pub type_lowering: RefCell<FxHashMap<(Ty<'tcx>, Option<VariantIdx>), TypeLowering<'ll>>>, @@ -115,10 +119,6 @@ fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode { } } -fn strip_powerpc64_vectors(data_layout: String) -> String { - data_layout.replace("-v256:256:256-v512:512:512", "") -} - pub unsafe fn create_module( tcx: TyCtxt<'_>, llcx: &'ll llvm::Context, @@ -130,7 +130,18 @@ pub unsafe fn create_module( let mut target_data_layout = sess.target.data_layout.clone(); if llvm_util::get_version() < (12, 0, 0) && sess.target.arch == "powerpc64" { - target_data_layout = strip_powerpc64_vectors(target_data_layout); + target_data_layout = target_data_layout.replace("-v256:256:256-v512:512:512", ""); + } + if llvm_util::get_version() < (13, 0, 0) { + if sess.target.arch == "powerpc64" { + target_data_layout = target_data_layout.replace("-S128", ""); + } + if sess.target.arch == "wasm32" { + target_data_layout = "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(); + } + if sess.target.arch == "wasm64" { + target_data_layout = "e-m:e-p:64:64-i64:64-n32:64-S128".to_string(); + } } // Ensure the data-layout values hardcoded remain the defaults. @@ -318,6 +329,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { const_globals: Default::default(), statics_to_rauw: RefCell::new(Vec::new()), used_statics: RefCell::new(Vec::new()), + compiler_used_statics: RefCell::new(Vec::new()), type_lowering: Default::default(), scalar_lltypes: Default::default(), pointee_infos: Default::default(), @@ -340,6 +352,18 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub fn coverage_context(&'a self) -> Option<&'a coverageinfo::CrateCoverageContext<'ll, 'tcx>> { self.coverage_cx.as_ref() } + + fn create_used_variable_impl(&self, name: &'static CStr, values: &[&'ll Value]) { + let section = cstr!("llvm.metadata"); + let array = self.const_array(&self.type_ptr_to(self.type_i8()), values); + + unsafe { + let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); + llvm::LLVMSetInitializer(g, array); + llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); + llvm::LLVMSetSection(g, section.as_ptr()); + } + } } impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { @@ -430,6 +454,10 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { &self.used_statics } + fn compiler_used_statics(&self) -> &RefCell<Vec<&'ll Value>> { + &self.compiler_used_statics + } + fn set_frame_pointer_type(&self, llfn: &'ll Value) { attributes::set_frame_pointer_type(self, llfn) } @@ -440,17 +468,14 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn create_used_variable(&self) { - let name = cstr!("llvm.used"); - let section = cstr!("llvm.metadata"); - let array = - self.const_array(&self.type_ptr_to(self.type_i8()), &*self.used_statics.borrow()); + self.create_used_variable_impl(cstr!("llvm.used"), &*self.used_statics.borrow()); + } - unsafe { - let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); - llvm::LLVMSetInitializer(g, array); - llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); - llvm::LLVMSetSection(g, section.as_ptr()); - } + fn create_compiler_used_variable(&self) { + self.create_used_variable_impl( + cstr!("llvm.compiler.used"), + &*self.compiler_used_statics.borrow(), + ); } fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function> { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index aa4db1622b2..1e6e5252b25 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -352,8 +352,8 @@ impl ModuleLlvm { impl Drop for ModuleLlvm { fn drop(&mut self) { unsafe { - llvm::LLVMContextDispose(&mut *(self.llcx as *mut _)); llvm::LLVMRustDisposeTargetMachine(&mut *(self.tm as *mut _)); + llvm::LLVMContextDispose(&mut *(self.llcx as *mut _)); } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs index ccd3e42e458..36aa022d746 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs @@ -6,7 +6,8 @@ pub use self::OptimizationDiagnosticKind::*; use crate::value::Value; use libc::c_uint; -use super::{DiagnosticInfo, Twine}; +use super::{DiagnosticInfo, SMDiagnostic}; +use rustc_span::InnerSpan; #[derive(Copy, Clone)] pub enum OptimizationDiagnosticKind { @@ -86,36 +87,91 @@ impl OptimizationDiagnostic<'ll> { } } -#[derive(Copy, Clone)] -pub struct InlineAsmDiagnostic<'ll> { +pub struct SrcMgrDiagnostic { + pub level: super::DiagnosticLevel, + pub message: String, + pub source: Option<(String, Vec<InnerSpan>)>, +} + +impl SrcMgrDiagnostic { + pub unsafe fn unpack(diag: &SMDiagnostic) -> SrcMgrDiagnostic { + // Recover the post-substitution assembly code from LLVM for better + // diagnostics. + let mut have_source = false; + let mut buffer = String::new(); + let mut level = super::DiagnosticLevel::Error; + let mut loc = 0; + let mut ranges = [0; 8]; + let mut num_ranges = ranges.len() / 2; + let message = super::build_string(|message| { + buffer = super::build_string(|buffer| { + have_source = super::LLVMRustUnpackSMDiagnostic( + diag, + message, + buffer, + &mut level, + &mut loc, + ranges.as_mut_ptr(), + &mut num_ranges, + ); + }) + .expect("non-UTF8 inline asm"); + }) + .expect("non-UTF8 SMDiagnostic"); + + SrcMgrDiagnostic { + message, + level, + source: have_source.then(|| { + let mut spans = vec![InnerSpan::new(loc as usize, loc as usize)]; + for i in 0..num_ranges { + spans.push(InnerSpan::new(ranges[i * 2] as usize, ranges[i * 2 + 1] as usize)); + } + (buffer, spans) + }), + } + } +} + +#[derive(Clone)] +pub struct InlineAsmDiagnostic { pub level: super::DiagnosticLevel, pub cookie: c_uint, - pub message: &'ll Twine, - pub instruction: Option<&'ll Value>, + pub message: String, + pub source: Option<(String, Vec<InnerSpan>)>, } -impl InlineAsmDiagnostic<'ll> { - unsafe fn unpack(di: &'ll DiagnosticInfo) -> Self { +impl InlineAsmDiagnostic { + unsafe fn unpackInlineAsm(di: &'ll DiagnosticInfo) -> Self { let mut cookie = 0; let mut message = None; - let mut instruction = None; let mut level = super::DiagnosticLevel::Error; - super::LLVMRustUnpackInlineAsmDiagnostic( - di, - &mut level, - &mut cookie, - &mut message, - &mut instruction, - ); + super::LLVMRustUnpackInlineAsmDiagnostic(di, &mut level, &mut cookie, &mut message); - InlineAsmDiagnostic { level, cookie, message: message.unwrap(), instruction } + InlineAsmDiagnostic { + level, + cookie, + message: super::twine_to_string(message.unwrap()), + source: None, + } + } + + unsafe fn unpackSrcMgr(di: &'ll DiagnosticInfo) -> Self { + let mut cookie = 0; + let smdiag = SrcMgrDiagnostic::unpack(super::LLVMRustGetSMDiagnostic(di, &mut cookie)); + InlineAsmDiagnostic { + level: smdiag.level, + cookie, + message: smdiag.message, + source: smdiag.source, + } } } pub enum Diagnostic<'ll> { Optimization(OptimizationDiagnostic<'ll>), - InlineAsm(InlineAsmDiagnostic<'ll>), + InlineAsm(InlineAsmDiagnostic), PGO(&'ll DiagnosticInfo), Linker(&'ll DiagnosticInfo), Unsupported(&'ll DiagnosticInfo), @@ -130,7 +186,7 @@ impl Diagnostic<'ll> { let kind = super::LLVMRustGetDiagInfoKind(di); match kind { - Dk::InlineAsm => InlineAsm(InlineAsmDiagnostic::unpack(di)), + Dk::InlineAsm => InlineAsm(InlineAsmDiagnostic::unpackInlineAsm(di)), Dk::OptimizationRemark => { Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)) @@ -162,6 +218,8 @@ impl Diagnostic<'ll> { Dk::Linker => Linker(di), Dk::Unsupported => Unsupported(di), + Dk::SrcMgr => InlineAsm(InlineAsmDiagnostic::unpackSrcMgr(di)), + _ => UnknownDiagnostic(di), } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 57173a49107..3f2ed02d90d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -490,6 +490,7 @@ pub enum DiagnosticKind { PGOProfile, Linker, Unsupported, + SrcMgr, } /// LLVMRustDiagnosticLevel @@ -2264,13 +2265,17 @@ extern "C" { level_out: &mut DiagnosticLevel, cookie_out: &mut c_uint, message_out: &mut Option<&'a Twine>, - instruction_out: &mut Option<&'a Value>, ); #[allow(improper_ctypes)] pub fn LLVMRustWriteDiagnosticInfoToString(DI: &DiagnosticInfo, s: &RustString); pub fn LLVMRustGetDiagInfoKind(DI: &DiagnosticInfo) -> DiagnosticKind; + pub fn LLVMRustGetSMDiagnostic( + DI: &'a DiagnosticInfo, + cookie_out: &mut c_uint, + ) -> &'a SMDiagnostic; + pub fn LLVMRustSetInlineAsmDiagnosticHandler( C: &Context, H: InlineAsmDiagHandler, diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 93456443aa0..8a8ece640fc 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -135,6 +135,11 @@ impl CodegenCx<'ll, 'tcx> { return false; } + // Match clang by only supporting COFF and ELF for now. + if self.tcx.sess.target.is_like_osx { + return false; + } + // Static relocation model should force copy relocations everywhere. if self.tcx.sess.relocation_model() == RelocModel::Static { return true; diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 0e036a432ad..930b4dc4d41 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -36,6 +36,6 @@ rustc_target = { path = "../rustc_target" } rustc_session = { path = "../rustc_session" } [dependencies.object] -version = "0.25.2" +version = "0.26.1" default-features = false features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"] diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 064a51c8f60..a5143a755fe 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -150,19 +150,8 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // trait upcasting coercion - // if both of the two `principal`s are `None`, this function would have returned early above. - // and if one of the two `principal`s is `None`, typechecking would have rejected this case. - let principal_a = data_a - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - let principal_b = data_b - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - - let vptr_entry_idx = cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot(( - principal_a.with_self_ty(cx.tcx(), source), - principal_b.with_self_ty(cx.tcx(), source), - )); + let vptr_entry_idx = + cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target)); if let Some(entry_idx) = vptr_entry_idx { let ptr_ty = cx.type_i8p(); diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 46f2adbe552..4266e42ec2b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -16,9 +16,11 @@ pub trait MiscMethods<'tcx>: BackendTypes { fn sess(&self) -> &Session; fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx>; fn used_statics(&self) -> &RefCell<Vec<Self::Value>>; + fn compiler_used_statics(&self) -> &RefCell<Vec<Self::Value>>; fn set_frame_pointer_type(&self, llfn: Self::Function); fn apply_target_cpu_attr(&self, llfn: Self::Function); fn create_used_variable(&self); + fn create_compiler_used_variable(&self); /// Declares the extern "C" main function for the entry point. Returns None if the symbol already exists. fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function>; } diff --git a/compiler/rustc_codegen_ssa/src/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs index 817fc02d166..a2a3cb56c78 100644 --- a/compiler/rustc_codegen_ssa/src/traits/statics.rs +++ b/compiler/rustc_codegen_ssa/src/traits/statics.rs @@ -6,17 +6,15 @@ pub trait StaticMethods: BackendTypes { fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value; fn codegen_static(&self, def_id: DefId, is_mutable: bool); - /// Mark the given global value as "used", to prevent a backend from potentially removing a - /// static variable that may otherwise appear unused. - /// - /// Static variables in Rust can be annotated with the `#[used]` attribute to direct the `rustc` - /// compiler to mark the variable as a "used global". - /// - /// ```no_run - /// #[used] - /// static FOO: u32 = 0; - /// ``` + /// Mark the given global value as "used", to prevent the compiler and linker from potentially + /// removing a static variable that may otherwise appear unused. fn add_used_global(&self, global: Self::Value); + + /// Same as add_used_global(), but only prevent the compiler from potentially removing an + /// otherwise unused symbol. The linker is still permitted to drop it. + /// + /// This corresponds to the semantics of the `#[used]` attribute. + fn add_compiler_used_global(&self, global: Self::Value); } pub trait StaticBuilderMethods: BackendTypes { diff --git a/compiler/rustc_error_codes/src/error_codes/E0161.md b/compiler/rustc_error_codes/src/error_codes/E0161.md index c2e2f0240f4..ebd2c97698b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0161.md +++ b/compiler/rustc_error_codes/src/error_codes/E0161.md @@ -4,11 +4,18 @@ Erroneous code example: ```compile_fail,E0161 #![feature(box_syntax)] +trait Bar { + fn f(self); +} + +impl Bar for i32 { + fn f(self) {} +} fn main() { - let array: &[isize] = &[1, 2, 3]; - let _x: Box<[isize]> = box *array; - // error: cannot move a value of type [isize]: the size of [isize] cannot + let b: Box<dyn Bar> = box (0 as i32); + b.f(); + // error: cannot move a value of type dyn Bar: the size of dyn Bar cannot // be statically determined } ``` @@ -22,8 +29,17 @@ it around as usual. Example: ``` #![feature(box_syntax)] +trait Bar { + fn f(&self); +} + +impl Bar for i32 { + fn f(&self) {} +} + fn main() { - let array: &[isize] = &[1, 2, 3]; - let _x: Box<&[isize]> = box array; // ok! + let b: Box<dyn Bar> = box (0 as i32); + b.f(); + // ok! } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0602.md b/compiler/rustc_error_codes/src/error_codes/E0602.md index dcaf251a96b..7980b704cae 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0602.md +++ b/compiler/rustc_error_codes/src/error_codes/E0602.md @@ -1,4 +1,4 @@ -An unknown lint was used on the command line. +An unknown or invalid lint was used on the command line. Erroneous code example: diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index f3c710e0f6a..18294dfad24 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -178,7 +178,7 @@ declare_features! ( /// Allows annotating functions conforming to `fn(&PanicInfo) -> !` with `#[panic_handler]`. /// This defines the behavior of panics. (accepted, panic_handler, "1.30.0", Some(44489), None), - /// Allows `#[used]` to preserve symbols (see llvm.used). + /// Allows `#[used]` to preserve symbols (see llvm.compiler.used). (accepted, used, "1.30.0", Some(40289), None), /// Allows `crate` in paths. (accepted, crate_in_paths, "1.30.0", Some(45477), None), diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 3a11b5a2144..01d84e287bc 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -22,7 +22,6 @@ // is also useful to track which value is the "expected" value in // terms of error reporting. -use super::equate::Equate; use super::glb::Glb; use super::lub::Lub; use super::sub::Sub; @@ -30,6 +29,7 @@ use super::type_variable::TypeVariableValue; use super::unify_key::replace_if_possible; use super::unify_key::{ConstVarValue, ConstVariableValue}; use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use super::{equate::Equate, type_variable::Diverging}; use super::{InferCtxt, MiscVariable, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; @@ -643,8 +643,13 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { .inner .borrow_mut() .type_variables() - .new_var(self.for_universe, false, origin); + .new_var(self.for_universe, Diverging::NotDiverging, origin); let u = self.tcx().mk_ty_var(new_var_id); + + // Record that we replaced `vid` with `new_var_id` as part of a generalization + // operation. This is needed to detect cyclic types. To see why, see the + // docs in the `type_variables` module. + self.infcx.inner.borrow_mut().type_variables().sub(vid, new_var_id); debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); Ok(u) } @@ -881,7 +886,7 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { *self.infcx.inner.borrow_mut().type_variables().var_origin(vid); let new_var_id = self.infcx.inner.borrow_mut().type_variables().new_var( self.for_universe, - false, + Diverging::NotDiverging, origin, ); let u = self.tcx().mk_ty_var(new_var_id); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 9b9ded5c6ba..9013bea749a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -23,7 +23,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; use rustc_middle::mir::interpret::EvalToConstValueResult; use rustc_middle::traits::select; -use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; @@ -46,7 +46,7 @@ use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, Veri use self::region_constraints::{ RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot, }; -use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use self::type_variable::{Diverging, TypeVariableOrigin, TypeVariableOriginKind}; pub mod at; pub mod canonical; @@ -671,6 +671,22 @@ pub struct CombinedSnapshot<'a, 'tcx> { } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + /// calls `tcx.try_unify_abstract_consts` after + /// canonicalizing the consts. + pub fn try_unify_abstract_consts( + &self, + a: ty::Unevaluated<'tcx>, + b: ty::Unevaluated<'tcx>, + ) -> bool { + let canonical = self.canonicalize_query( + ((a.def, a.substs), (b.def, b.substs)), + &mut OriginalQueryValues::default(), + ); + debug!("canonical consts: {:?}", &canonical.value); + + self.tcx.try_unify_abstract_consts(canonical.value) + } + pub fn is_in_snapshot(&self) -> bool { self.in_snapshot.get() } @@ -679,10 +695,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { t.fold_with(&mut self.freshener()) } - pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool { + /// Returns whether `ty` is a diverging type variable or not. + /// (If `ty` is not a type variable at all, returns not diverging.) + /// + /// No attempt is made to resolve `ty`. + pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> Diverging { match *ty.kind() { ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid), - _ => false, + _ => Diverging::NotDiverging, + } + } + + /// Returns the origin of the type variable identified by `vid`, or `None` + /// if this is not a type variable. + /// + /// No attempt is made to resolve `ty`. + pub fn type_var_origin(&'a self, ty: Ty<'tcx>) -> Option<TypeVariableOrigin> { + match *ty.kind() { + ty::Infer(ty::TyVar(vid)) => { + Some(*self.inner.borrow_mut().type_variables().var_origin(vid)) + } + _ => None, } } @@ -695,28 +728,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { freshen::TypeFreshener::new(self, true) } - pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric { - use rustc_middle::ty::error::UnconstrainedNumeric::Neither; - use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; - match *ty.kind() { - ty::Infer(ty::IntVar(vid)) => { - if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() { - Neither - } else { - UnconstrainedInt - } - } - ty::Infer(ty::FloatVar(vid)) => { - if self.inner.borrow_mut().float_unification_table().probe_value(vid).is_some() { - Neither - } else { - UnconstrainedFloat - } - } - _ => Neither, - } - } - pub fn unsolved_variables(&self) -> Vec<Ty<'tcx>> { let mut inner = self.inner.borrow_mut(); let mut vars: Vec<Ty<'_>> = inner @@ -969,29 +980,62 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); } + /// Processes a `Coerce` predicate from the fulfillment context. + /// This is NOT the preferred way to handle coercion, which is to + /// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`). + /// + /// This method here is actually a fallback that winds up being + /// invoked when `FnCtxt::coerce` encounters unresolved type variables + /// and records a coercion predicate. Presently, this method is equivalent + /// to `subtype_predicate` -- that is, "coercing" `a` to `b` winds up + /// actually requiring `a <: b`. This is of course a valid coercion, + /// but it's not as flexible as `FnCtxt::coerce` would be. + /// + /// (We may refactor this in the future, but there are a number of + /// practical obstacles. Among other things, `FnCtxt::coerce` presently + /// records adjustments that are required on the HIR in order to perform + /// the coercion, and we don't currently have a way to manage that.) + pub fn coerce_predicate( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + predicate: ty::PolyCoercePredicate<'tcx>, + ) -> Option<InferResult<'tcx, ()>> { + let subtype_predicate = predicate.map_bound(|p| ty::SubtypePredicate { + a_is_expected: false, // when coercing from `a` to `b`, `b` is expected + a: p.a, + b: p.b, + }); + self.subtype_predicate(cause, param_env, subtype_predicate) + } + pub fn subtype_predicate( &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, predicate: ty::PolySubtypePredicate<'tcx>, ) -> Option<InferResult<'tcx, ()>> { - // Subtle: it's ok to skip the binder here and resolve because - // `shallow_resolve` just ignores anything that is not a type - // variable, and because type variable's can't (at present, at + // Check for two unresolved inference variables, in which case we can + // make no progress. This is partly a micro-optimization, but it's + // also an opportunity to "sub-unify" the variables. This isn't + // *necessary* to prevent cycles, because they would eventually be sub-unified + // anyhow during generalization, but it helps with diagnostics (we can detect + // earlier that they are sub-unified). + // + // Note that we can just skip the binders here because + // type variables can't (at present, at // least) capture any of the things bound by this binder. // - // NOTE(nmatsakis): really, there is no *particular* reason to do this - // `shallow_resolve` here except as a micro-optimization. - // Naturally I could not resist. - let two_unbound_type_vars = { - let a = self.shallow_resolve(predicate.skip_binder().a); - let b = self.shallow_resolve(predicate.skip_binder().b); - a.is_ty_var() && b.is_ty_var() - }; - - if two_unbound_type_vars { - // Two unbound type variables? Can't make progress. - return None; + // Note that this sub here is not just for diagnostics - it has semantic + // effects as well. + let r_a = self.shallow_resolve(predicate.skip_binder().a); + let r_b = self.shallow_resolve(predicate.skip_binder().b); + match (r_a.kind(), r_b.kind()) { + (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { + self.inner.borrow_mut().type_variables().sub(a_vid, b_vid); + return None; + } + _ => {} } Some(self.commit_if_ok(|_snapshot| { @@ -1020,12 +1064,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }) } - pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid { + pub fn next_ty_var_id(&self, diverging: Diverging, origin: TypeVariableOrigin) -> TyVid { self.inner.borrow_mut().type_variables().new_var(self.universe(), diverging, origin) } pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { - self.tcx.mk_ty_var(self.next_ty_var_id(false, origin)) + self.tcx.mk_ty_var(self.next_ty_var_id(Diverging::NotDiverging, origin)) } pub fn next_ty_var_in_universe( @@ -1033,12 +1077,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: TypeVariableOrigin, universe: ty::UniverseIndex, ) -> Ty<'tcx> { - let vid = self.inner.borrow_mut().type_variables().new_var(universe, false, origin); + let vid = self.inner.borrow_mut().type_variables().new_var( + universe, + Diverging::NotDiverging, + origin, + ); self.tcx.mk_ty_var(vid) } pub fn next_diverging_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { - self.tcx.mk_ty_var(self.next_ty_var_id(true, origin)) + self.tcx.mk_ty_var(self.next_ty_var_id(Diverging::Diverges, origin)) } pub fn next_const_var( @@ -1152,7 +1200,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // as the substitutions for the default, `(T, U)`. let ty_var_id = self.inner.borrow_mut().type_variables().new_var( self.universe(), - false, + Diverging::NotDiverging, TypeVariableOrigin { kind: TypeVariableOriginKind::TypeParameterDefinition( param.name, diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 20be06adfd0..261c3471a98 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -22,6 +22,7 @@ //! constituents) use crate::infer::combine::ConstEquateRelation; +use crate::infer::type_variable::Diverging; use crate::infer::InferCtxt; use crate::infer::{ConstVarValue, ConstVariableValue}; use rustc_data_structures::fx::FxHashMap; @@ -920,7 +921,8 @@ where // Replacing with a new variable in the universe `self.universe`, // it will be unified later with the original type variable in // the universe `_universe`. - let new_var_id = variables.new_var(self.universe, false, origin); + let new_var_id = + variables.new_var(self.universe, Diverging::NotDiverging, origin); let u = self.tcx().mk_ty_var(new_var_id); debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 07c75d50d91..4dd5e8ba545 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -19,6 +19,7 @@ pub fn explicit_outlives_bounds<'tcx>( .filter_map(move |kind| match kind { ty::PredicateKind::Projection(..) | ty::PredicateKind::Trait(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index 2f126d89569..1692d8ee526 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -85,7 +85,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); match (a.kind(), b.kind()) { - (&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => { + (&ty::Infer(TyVar(_)), &ty::Infer(TyVar(_))) => { // Shouldn't have any LBR here, so we can safely put // this under a binder below without fear of accidental // capture. @@ -93,11 +93,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { assert!(!b.has_escaping_bound_vars()); // can't make progress on `A <: B` if both A and B are - // type variables, so record an obligation. We also - // have to record in the `type_variables` tracker that - // the two variables are equal modulo subtyping, which - // is important to the occurs check later on. - infcx.inner.borrow_mut().type_variables().sub(a_vid, b_vid); + // type variables, so record an obligation. self.fields.obligations.push(Obligation::new( self.fields.trace.cause.clone(), self.fields.param_env, diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 13b78b26af4..d2b0bdaf978 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -75,14 +75,30 @@ pub struct TypeVariableStorage<'tcx> { /// ?1 <: ?3 /// Box<?3> <: ?1 /// - /// This works because `?1` and `?3` are unified in the - /// `sub_relations` relation (not in `eq_relations`). Then when we - /// process the `Box<?3> <: ?1` constraint, we do an occurs check - /// on `Box<?3>` and find a potential cycle. + /// Without this second table, what would happen in a case like + /// this is that we would instantiate `?1` with a generalized + /// type like `Box<?6>`. We would then relate `Box<?3> <: Box<?6>` + /// and infer that `?3 <: ?6`. Next, since `?1` was instantiated, + /// we would process `?1 <: ?3`, generalize `?1 = Box<?6>` to `Box<?9>`, + /// and instantiate `?3` with `Box<?9>`. Finally, we would relate + /// `?6 <: ?9`. But now that we instantiated `?3`, we can process + /// `?3 <: ?6`, which gives us `Box<?9> <: ?6`... and the cycle + /// continues. (This is `occurs-check-2.rs`.) + /// + /// What prevents this cycle is that when we generalize + /// `Box<?3>` to `Box<?6>`, we also sub-unify `?3` and `?6` + /// (in the generalizer). When we then process `Box<?6> <: ?3`, + /// the occurs check then fails because `?6` and `?3` are sub-unified, + /// and hence generalization fails. /// /// This is reasonable because, in Rust, subtypes have the same /// "skeleton" and hence there is no possible type such that /// (e.g.) `Box<?3> <: ?3` for any `?3`. + /// + /// In practice, we sometimes sub-unify variables in other spots, such + /// as when processing subtype predicates. This is not necessary but is + /// done to aid diagnostics, as it allows us to be more effective when + /// we guide the user towards where they should insert type hints. sub_relations: ut::UnificationTableStorage<ty::TyVid>, } @@ -119,7 +135,13 @@ pub enum TypeVariableOriginKind { pub(crate) struct TypeVariableData { origin: TypeVariableOrigin, - diverging: bool, + diverging: Diverging, +} + +#[derive(Copy, Clone, Debug)] +pub enum Diverging { + NotDiverging, + Diverges, } #[derive(Copy, Clone, Debug)] @@ -173,7 +195,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// /// Note that this function does not return care whether /// `vid` has been unified with something else or not. - pub fn var_diverges(&self, vid: ty::TyVid) -> bool { + pub fn var_diverges(&self, vid: ty::TyVid) -> Diverging { self.storage.values.get(vid.index as usize).diverging } @@ -238,7 +260,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { pub fn new_var( &mut self, universe: ty::UniverseIndex, - diverging: bool, + diverging: Diverging, origin: TypeVariableOrigin, ) -> ty::TyVid { let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 3139e121163..3a25cb66896 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -158,6 +158,10 @@ impl Elaborator<'tcx> { // Currently, we do not "elaborate" predicates like `X <: Y`, // though conceivably we might. } + ty::PredicateKind::Coerce(..) => { + // Currently, we do not "elaborate" predicates like `X -> Y`, + // though conceivably we might. + } ty::PredicateKind::Projection(..) => { // Nothing to elaborate in a projection predicate. } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 77c7040e6a7..5a72db7752d 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1652,6 +1652,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { ObjectSafe(..) | ClosureKind(..) | Subtype(..) | + Coerce(..) | ConstEvaluatable(..) | ConstEquate(..) | TypeWellFormedFromEnv(..) => continue, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 47c6e904cd7..c2a46e997df 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -332,7 +332,16 @@ impl LintStore { crate_attrs: &[ast::Attribute], ) { let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name); - + if lint_name_only == crate::WARNINGS.name_lower() && level == Level::ForceWarn { + return struct_span_err!( + sess, + DUMMY_SP, + E0602, + "`{}` lint group is not supported with ´--force-warn´", + crate::WARNINGS.name_lower() + ) + .emit(); + } let db = match self.check_lint_name(sess, lint_name_only, tool_name, crate_attrs) { CheckLintNameResult::Ok(_) => None, CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)), diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 2c039b6d05d..edb158dd378 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -18,23 +18,27 @@ declare_lint! { /// /// ### Explanation /// - /// `Drop` bounds do not really accomplish anything. A type may have - /// compiler-generated drop glue without implementing the `Drop` trait - /// itself. The `Drop` trait also only has one method, `Drop::drop`, and - /// that function is by fiat not callable in user code. So there is really - /// no use case for using `Drop` in trait bounds. + /// A generic trait bound of the form `T: Drop` is most likely misleading + /// and not what the programmer intended (they probably should have used + /// `std::mem::needs_drop` instead). /// - /// The most likely use case of a drop bound is to distinguish between - /// types that have destructors and types that don't. Combined with - /// specialization, a naive coder would write an implementation that - /// assumed a type could be trivially dropped, then write a specialization - /// for `T: Drop` that actually calls the destructor. Except that doing so - /// is not correct; String, for example, doesn't actually implement Drop, - /// but because String contains a Vec, assuming it can be trivially dropped - /// will leak memory. + /// `Drop` bounds do not actually indicate whether a type can be trivially + /// dropped or not, because a composite type containing `Drop` types does + /// not necessarily implement `Drop` itself. Naïvely, one might be tempted + /// to write an implementation that assumes that a type can be trivially + /// dropped while also supplying a specialization for `T: Drop` that + /// actually calls the destructor. However, this breaks down e.g. when `T` + /// is `String`, which does not implement `Drop` itself but contains a + /// `Vec`, which does implement `Drop`, so assuming `T` can be trivially + /// dropped would lead to a memory leak here. + /// + /// Furthermore, the `Drop` trait only contains one method, `Drop::drop`, + /// which may not be called explicitly in user code (`E0040`), so there is + /// really no use case for using `Drop` in trait bounds, save perhaps for + /// some obscure corner cases, which can use `#[allow(drop_bounds)]`. pub DROP_BOUNDS, Warn, - "bounds of the form `T: Drop` are useless" + "bounds of the form `T: Drop` are most likely incorrect" } declare_lint! { @@ -102,8 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { None => return, }; let msg = format!( - "bounds on `{}` are useless, consider instead \ - using `{}` to detect if a type has a destructor", + "bounds on `{}` are most likely incorrect, consider instead \ + using `{}` to detect whether a type can be trivially dropped", predicate, cx.tcx.def_path_str(needs_drop) ); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index f563870e3e0..b3f86f3295a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -922,9 +922,17 @@ LLVMRustOptimizeWithNewPassManager( MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); MPM.addPass(ModuleAddressSanitizerPass( /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); +#if LLVM_VERSION_GE(14, 0) + AddressSanitizerOptions opts(/*CompileKernel=*/false, + SanitizerOptions->SanitizeAddressRecover, + /*UseAfterScope=*/true, + AsanDetectStackUseAfterReturnMode::Runtime); + MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass(opts))); +#else MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass( /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, /*UseAfterScope=*/true))); +#endif } ); #else @@ -952,8 +960,15 @@ LLVMRustOptimizeWithNewPassManager( #if LLVM_VERSION_GE(11, 0) OptimizerLastEPCallbacks.push_back( [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { +#if LLVM_VERSION_GE(14, 0) + HWAddressSanitizerOptions opts( + /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover, + /*DisableOptimization=*/false); + MPM.addPass(HWAddressSanitizerPass(opts)); +#else MPM.addPass(HWAddressSanitizerPass( /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover)); +#endif } ); #else diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 52566b19eca..4edfed03401 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1114,15 +1114,13 @@ extern "C" void LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI, LLVMRustDiagnosticLevel *LevelOut, unsigned *CookieOut, - LLVMTwineRef *MessageOut, - LLVMValueRef *InstructionOut) { + LLVMTwineRef *MessageOut) { // Undefined to call this not on an inline assembly diagnostic! llvm::DiagnosticInfoInlineAsm *IA = static_cast<llvm::DiagnosticInfoInlineAsm *>(unwrap(DI)); *CookieOut = IA->getLocCookie(); *MessageOut = wrap(&IA->getMsgStr()); - *InstructionOut = wrap(IA->getInstruction()); switch (IA->getSeverity()) { case DS_Error: @@ -1165,6 +1163,7 @@ enum class LLVMRustDiagnosticKind { PGOProfile, Linker, Unsupported, + SrcMgr, }; static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) { @@ -1193,6 +1192,10 @@ static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) { return LLVMRustDiagnosticKind::Linker; case DK_Unsupported: return LLVMRustDiagnosticKind::Unsupported; +#if LLVM_VERSION_GE(13, 0) + case DK_SrcMgr: + return LLVMRustDiagnosticKind::SrcMgr; +#endif default: return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark) ? LLVMRustDiagnosticKind::OptimizationRemarkOther @@ -1280,6 +1283,17 @@ extern "C" void LLVMRustSetInlineAsmDiagnosticHandler( #endif } +extern "C" LLVMSMDiagnosticRef LLVMRustGetSMDiagnostic( + LLVMDiagnosticInfoRef DI, unsigned *Cookie) { +#if LLVM_VERSION_GE(13, 0) + llvm::DiagnosticInfoSrcMgr *SM = static_cast<llvm::DiagnosticInfoSrcMgr *>(unwrap(DI)); + *Cookie = SM->getLocCookie(); + return wrap(&SM->getSMDiag()); +#else + report_fatal_error("Shouldn't get called on older versions"); +#endif +} + extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, RustStringRef MessageOut, RustStringRef BufferOut, diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index dcd36d61bc6..7ad36973f46 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -1,6 +1,6 @@ use proc_macro::TokenStream; use proc_macro2::{Delimiter, TokenTree}; -use quote::quote; +use quote::{quote, quote_spanned}; use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; @@ -42,19 +42,19 @@ enum QueryModifier { LoadCached(Ident, Ident, Block), /// A cycle error for this query aborting the compilation with a fatal error. - FatalCycle, + FatalCycle(Ident), /// A cycle error results in a delay_bug call - CycleDelayBug, + CycleDelayBug(Ident), /// Don't hash the result, instead just mark a query red if it runs - NoHash, + NoHash(Ident), /// Generate a dep node based on the dependencies of the query - Anon, + Anon(Ident), /// Always evaluate the query, ignoring its dependencies - EvalAlways, + EvalAlways(Ident), } impl Parse for QueryModifier { @@ -111,15 +111,15 @@ impl Parse for QueryModifier { let ty = args.parse()?; Ok(QueryModifier::Storage(ty)) } else if modifier == "fatal_cycle" { - Ok(QueryModifier::FatalCycle) + Ok(QueryModifier::FatalCycle(modifier)) } else if modifier == "cycle_delay_bug" { - Ok(QueryModifier::CycleDelayBug) + Ok(QueryModifier::CycleDelayBug(modifier)) } else if modifier == "no_hash" { - Ok(QueryModifier::NoHash) + Ok(QueryModifier::NoHash(modifier)) } else if modifier == "anon" { - Ok(QueryModifier::Anon) + Ok(QueryModifier::Anon(modifier)) } else if modifier == "eval_always" { - Ok(QueryModifier::EvalAlways) + Ok(QueryModifier::EvalAlways(modifier)) } else { Err(Error::new(modifier.span(), "unknown query modifier")) } @@ -203,19 +203,19 @@ struct QueryModifiers { load_cached: Option<(Ident, Ident, Block)>, /// A cycle error for this query aborting the compilation with a fatal error. - fatal_cycle: bool, + fatal_cycle: Option<Ident>, /// A cycle error results in a delay_bug call - cycle_delay_bug: bool, + cycle_delay_bug: Option<Ident>, /// Don't hash the result, instead just mark a query red if it runs - no_hash: bool, + no_hash: Option<Ident>, /// Generate a dep node based on the dependencies of the query - anon: bool, + anon: Option<Ident>, // Always evaluate the query, ignoring its dependencies - eval_always: bool, + eval_always: Option<Ident>, } /// Process query modifiers into a struct, erroring on duplicates @@ -224,11 +224,11 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers { let mut storage = None; let mut cache = None; let mut desc = None; - let mut fatal_cycle = false; - let mut cycle_delay_bug = false; - let mut no_hash = false; - let mut anon = false; - let mut eval_always = false; + let mut fatal_cycle = None; + let mut cycle_delay_bug = None; + let mut no_hash = None; + let mut anon = None; + let mut eval_always = None; for modifier in query.modifiers.0.drain(..) { match modifier { QueryModifier::LoadCached(tcx, id, block) => { @@ -289,35 +289,35 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers { } desc = Some((tcx, list)); } - QueryModifier::FatalCycle => { - if fatal_cycle { + QueryModifier::FatalCycle(ident) => { + if fatal_cycle.is_some() { panic!("duplicate modifier `fatal_cycle` for query `{}`", query.name); } - fatal_cycle = true; + fatal_cycle = Some(ident); } - QueryModifier::CycleDelayBug => { - if cycle_delay_bug { + QueryModifier::CycleDelayBug(ident) => { + if cycle_delay_bug.is_some() { panic!("duplicate modifier `cycle_delay_bug` for query `{}`", query.name); } - cycle_delay_bug = true; + cycle_delay_bug = Some(ident); } - QueryModifier::NoHash => { - if no_hash { + QueryModifier::NoHash(ident) => { + if no_hash.is_some() { panic!("duplicate modifier `no_hash` for query `{}`", query.name); } - no_hash = true; + no_hash = Some(ident); } - QueryModifier::Anon => { - if anon { + QueryModifier::Anon(ident) => { + if anon.is_some() { panic!("duplicate modifier `anon` for query `{}`", query.name); } - anon = true; + anon = Some(ident); } - QueryModifier::EvalAlways => { - if eval_always { + QueryModifier::EvalAlways(ident) => { + if eval_always.is_some() { panic!("duplicate modifier `eval_always` for query `{}`", query.name); } - eval_always = true; + eval_always = Some(ident); } } } @@ -454,31 +454,39 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let mut attributes = Vec::new(); // Pass on the fatal_cycle modifier - if modifiers.fatal_cycle { - attributes.push(quote! { fatal_cycle }); + if let Some(fatal_cycle) = &modifiers.fatal_cycle { + attributes.push(quote! { #fatal_cycle }); }; // Pass on the storage modifier if let Some(ref ty) = modifiers.storage { - attributes.push(quote! { storage(#ty) }); + let span = ty.span(); + attributes.push(quote_spanned! {span=> storage(#ty) }); }; // Pass on the cycle_delay_bug modifier - if modifiers.cycle_delay_bug { - attributes.push(quote! { cycle_delay_bug }); + if let Some(cycle_delay_bug) = &modifiers.cycle_delay_bug { + attributes.push(quote! { #cycle_delay_bug }); }; // Pass on the no_hash modifier - if modifiers.no_hash { - attributes.push(quote! { no_hash }); + if let Some(no_hash) = &modifiers.no_hash { + attributes.push(quote! { #no_hash }); }; // Pass on the anon modifier - if modifiers.anon { - attributes.push(quote! { anon }); + if let Some(anon) = &modifiers.anon { + attributes.push(quote! { #anon }); }; // Pass on the eval_always modifier - if modifiers.eval_always { - attributes.push(quote! { eval_always }); + if let Some(eval_always) = &modifiers.eval_always { + attributes.push(quote! { #eval_always }); }; - let attribute_stream = quote! {#(#attributes),*}; + // This uses the span of the query definition for the commas, + // which can be important if we later encounter any ambiguity + // errors with any of the numerous macro_rules! macros that + // we use. Using the call-site span would result in a span pointing + // at the entire `rustc_queries!` invocation, which wouldn't + // be very useful. + let span = name.span(); + let attribute_stream = quote_spanned! {span=> #(#attributes),*}; let doc_comments = query.doc_comments.iter(); // Add the query to the group query_stream.extend(quote! { diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index f44267a404b..ffa26b9f299 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -189,7 +189,7 @@ impl Scope { // To avoid issues with macro-generated spans, the span // of the statement must be nested in that of the block. if span.lo() <= stmt_span.lo() && stmt_span.lo() <= span.hi() { - return Span::new(stmt_span.lo(), span.hi(), span.ctxt()); + return span.with_lo(stmt_span.lo()); } } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5b37556985b..85b1274da10 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -987,9 +987,9 @@ rustc_queries! { desc { |tcx| "finding all vtable entries for trait {}", tcx.def_path_str(key.def_id()) } } - query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>)) -> Option<usize> { - desc { |tcx| "finding the slot within vtable for trait {} vtable ptr during trait upcasting coercion from {} vtable", - tcx.def_path_str(key.1.def_id()), tcx.def_path_str(key.0.def_id()) } + query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::Ty<'tcx>, ty::Ty<'tcx>)) -> Option<usize> { + desc { |tcx| "finding the slot within vtable for trait object {} vtable ptr during trait upcasting coercion from {} vtable", + key.1, key.0 } } query codegen_fulfill_obligation( diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index a4a2e824637..469da858ccf 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -225,6 +225,8 @@ pub enum ObligationCauseCode<'tcx> { SizedReturnType, /// Yield type must be `Sized`. SizedYieldType, + /// Box expression result type must be `Sized`. + SizedBoxType, /// Inline asm operand type must be `Sized`. InlineAsmSized, /// `[T, ..n]` implies that `T` must be `Copy`. @@ -503,6 +505,9 @@ pub enum ImplSource<'tcx, N> { /// Successful resolution for a builtin trait. Builtin(ImplSourceBuiltinData<N>), + /// ImplSource for trait upcasting coercion + TraitUpcasting(ImplSourceTraitUpcastingData<'tcx, N>), + /// ImplSource automatically generated for a closure. The `DefId` is the ID /// of the closure expression. This is a `ImplSource::UserDefined` in spirit, but the /// impl is generated by the compiler and does not appear in the source. @@ -538,6 +543,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) | ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(), ImplSource::TraitAlias(d) => d.nested, + ImplSource::TraitUpcasting(d) => d.nested, } } @@ -554,6 +560,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) | ImplSource::Pointee(ImplSourcePointeeData) => &[], ImplSource::TraitAlias(d) => &d.nested[..], + ImplSource::TraitUpcasting(d) => &d.nested[..], } } @@ -605,6 +612,13 @@ impl<'tcx, N> ImplSource<'tcx, N> { substs: d.substs, nested: d.nested.into_iter().map(f).collect(), }), + ImplSource::TraitUpcasting(d) => { + ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { + upcast_trait_ref: d.upcast_trait_ref, + vtable_vptr_slot: d.vtable_vptr_slot, + nested: d.nested.into_iter().map(f).collect(), + }) + } } } } @@ -651,6 +665,20 @@ pub struct ImplSourceAutoImplData<N> { } #[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceTraitUpcastingData<'tcx, N> { + /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. + pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, + + /// The vtable is formed by concatenating together the method lists of + /// the base object trait and all supertraits, pointers to supertrait vtable will + /// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable + /// within that vtable. + pub vtable_vptr_slot: Option<usize>, + + pub nested: Vec<N>, +} + +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] pub struct ImplSourceBuiltinData<N> { pub nested: Vec<N>, } @@ -661,8 +689,9 @@ pub struct ImplSourceObjectData<'tcx, N> { pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, /// The vtable is formed by concatenating together the method lists of - /// the base object trait and all supertraits; this is the start of - /// `upcast_trait_ref`'s methods in that vtable. + /// the base object trait and all supertraits, pointers to supertrait vtable will + /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods + /// in that vtable. pub vtable_base: usize, pub nested: Vec<N>, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 924568a01a7..3b7c201f3ee 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -135,6 +135,11 @@ pub enum SelectionCandidate<'tcx> { /// `rustc_infer::traits::util::supertraits`. ObjectCandidate(usize), + /// Perform trait upcasting coercion of `dyn Trait` to a supertrait of `Trait`. + /// The index is the position in the iterator returned by + /// `rustc_infer::traits::util::supertraits`. + TraitUpcastingUnsizeCandidate(usize), + BuiltinObjectCandidate, BuiltinUnsizeCandidate, diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index 4f978e63630..aa16e14fedc 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -30,6 +30,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d), super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d), + + super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), } } } @@ -70,6 +72,16 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceBuiltinData<N> { } } +impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitUpcastingData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceTraitUpcastingData(upcast={:?}, vtable_vptr_slot={:?}, nested={:?})", + self.upcast_trait_ref, self.vtable_vptr_slot, self.nested + ) + } +} + impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceAutoImplData<N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 3846d9ffdba..1aa6c84dbc4 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -71,12 +71,6 @@ pub enum TypeError<'tcx> { TargetFeatureCast(DefId), } -pub enum UnconstrainedNumeric { - UnconstrainedFloat, - UnconstrainedInt, - Neither, -} - /// Explains the source of a type err in a short, human readable way. This is meant to be placed /// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()` /// afterwards to present additional details, particularly when it comes to lifetime-related diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 391c8292bd5..04df706d908 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -231,6 +231,10 @@ impl FlagComputation { self.add_ty(a); self.add_ty(b); } + ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { + self.add_ty(a); + self.add_ty(b); + } ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { self.add_projection_ty(projection_ty); self.add_ty(ty); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0906cffa05c..9fcf35b7320 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -485,8 +485,22 @@ pub enum PredicateKind<'tcx> { ClosureKind(DefId, SubstsRef<'tcx>, ClosureKind), /// `T1 <: T2` + /// + /// This obligation is created most often when we have two + /// unresolved type variables and hence don't have enough + /// information to process the subtyping obligation yet. Subtype(SubtypePredicate<'tcx>), + /// `T1` coerced to `T2` + /// + /// Like a subtyping obligation, this is created most often + /// when we have two unresolved type variables and hence + /// don't have enough information to process the coercion + /// obligation yet. At the moment, we actually process coercions + /// very much like subtyping and don't handle the full coercion + /// logic. + Coerce(CoercePredicate<'tcx>), + /// Constant initializer must evaluate successfully. ConstEvaluatable(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>), @@ -655,6 +669,9 @@ pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<Ty<'tcx>, ty::Region<'t pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>; pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>; +/// Encodes that `a` must be a subtype of `b`. The `a_is_expected` flag indicates +/// whether the `a` type is the type that we should label as "expected" when +/// presenting user diagnostics. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub struct SubtypePredicate<'tcx> { @@ -664,6 +681,15 @@ pub struct SubtypePredicate<'tcx> { } pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>; +/// Encodes that we have to coerce *from* the `a` type to the `b` type. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct CoercePredicate<'tcx> { + pub a: Ty<'tcx>, + pub b: Ty<'tcx>, +} +pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; + /// This kind of predicate has no *direct* correspondent in the /// syntax, but it roughly corresponds to the syntactic forms: /// @@ -806,6 +832,7 @@ impl<'tcx> Predicate<'tcx> { } PredicateKind::Projection(..) | PredicateKind::Subtype(..) + | PredicateKind::Coerce(..) | PredicateKind::RegionOutlives(..) | PredicateKind::WellFormed(..) | PredicateKind::ObjectSafe(..) @@ -824,6 +851,7 @@ impl<'tcx> Predicate<'tcx> { PredicateKind::Trait(..) | PredicateKind::Projection(..) | PredicateKind::Subtype(..) + | PredicateKind::Coerce(..) | PredicateKind::RegionOutlives(..) | PredicateKind::WellFormed(..) | PredicateKind::ObjectSafe(..) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c96621338f5..8558d6bb00e 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2236,6 +2236,10 @@ define_print_and_forward_display! { p!(print(self.a), " <: ", print(self.b)) } + ty::CoercePredicate<'tcx> { + p!(print(self.a), " -> ", print(self.b)) + } + ty::TraitPredicate<'tcx> { p!(print(self.trait_ref.self_ty()), ": ", print(self.trait_ref.print_only_trait_path())) @@ -2268,6 +2272,7 @@ define_print_and_forward_display! { p!(print(data)) } ty::PredicateKind::Subtype(predicate) => p!(print(predicate)), + ty::PredicateKind::Coerce(predicate) => p!(print(predicate)), ty::PredicateKind::RegionOutlives(predicate) => p!(print(predicate)), ty::PredicateKind::TypeOutlives(predicate) => p!(print(predicate)), ty::PredicateKind::Projection(predicate) => p!(print(predicate)), diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 62397752db2..a46cac1e7f7 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -179,6 +179,7 @@ impl fmt::Debug for ty::PredicateKind<'tcx> { match *self { ty::PredicateKind::Trait(ref a) => a.fmt(f), ty::PredicateKind::Subtype(ref pair) => pair.fmt(f), + ty::PredicateKind::Coerce(ref pair) => pair.fmt(f), ty::PredicateKind::RegionOutlives(ref pair) => pair.fmt(f), ty::PredicateKind::TypeOutlives(ref pair) => pair.fmt(f), ty::PredicateKind::Projection(ref pair) => pair.fmt(f), @@ -380,6 +381,13 @@ impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> { } } +impl<'a, 'tcx> Lift<'tcx> for ty::CoercePredicate<'a> { + type Lifted = ty::CoercePredicate<'tcx>; + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<ty::CoercePredicate<'tcx>> { + tcx.lift((self.a, self.b)).map(|(a, b)| ty::CoercePredicate { a, b }) + } +} + impl<'tcx, A: Copy + Lift<'tcx>, B: Copy + Lift<'tcx>> Lift<'tcx> for ty::OutlivesPredicate<A, B> { type Lifted = ty::OutlivesPredicate<A::Lifted, B::Lifted>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { @@ -420,6 +428,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> { match self { ty::PredicateKind::Trait(data) => tcx.lift(data).map(ty::PredicateKind::Trait), ty::PredicateKind::Subtype(data) => tcx.lift(data).map(ty::PredicateKind::Subtype), + ty::PredicateKind::Coerce(data) => tcx.lift(data).map(ty::PredicateKind::Coerce), ty::PredicateKind::RegionOutlives(data) => { tcx.lift(data).map(ty::PredicateKind::RegionOutlives) } diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index d05e0135dfe..35bb6ef6c2d 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -1893,9 +1893,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // While this is located in `nll::typeck` this error is not // an NLL error, it's a required check to prevent creation - // of unsized rvalues in certain cases: - // * operand of a box expression - // * callee in a call expression + // of unsized rvalues in a call expression. diag.emit(); } } diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs index cee4c2e30aa..697e98311e2 100644 --- a/compiler/rustc_mir/src/interpret/cast.rs +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -275,16 +275,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return self.write_immediate(*val, dest); } // trait upcasting coercion - let principal_a = data_a.principal().expect( - "unsize_into_ptr: missing principal trait for trait upcasting coercion", - ); - let principal_b = data_b.principal().expect( - "unsize_into_ptr: missing principal trait for trait upcasting coercion", - ); - let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( - principal_a.with_self_ty(*self.tcx, src_pointee_ty), - principal_b.with_self_ty(*self.tcx, src_pointee_ty), + src_pointee_ty, + dest_pointee_ty, )); if let Some(entry_idx) = vptr_entry_idx { diff --git a/compiler/rustc_mir/src/transform/add_call_guards.rs b/compiler/rustc_mir/src/transform/add_call_guards.rs index 1dddaeb89e6..12ee6bb4c67 100644 --- a/compiler/rustc_mir/src/transform/add_call_guards.rs +++ b/compiler/rustc_mir/src/transform/add_call_guards.rs @@ -38,7 +38,9 @@ impl<'tcx> MirPass<'tcx> for AddCallGuards { impl AddCallGuards { pub fn add_call_guards(&self, body: &mut Body<'_>) { - let pred_count: IndexVec<_, _> = body.predecessors().iter().map(|ps| ps.len()).collect(); + let mut pred_count: IndexVec<_, _> = + body.predecessors().iter().map(|ps| ps.len()).collect(); + pred_count[START_BLOCK] += 1; // We need a place to store the new blocks generated let mut new_blocks = Vec::new(); diff --git a/compiler/rustc_mir/src/transform/check_consts/check.rs b/compiler/rustc_mir/src/transform/check_consts/check.rs index 48fd63b258c..09e908e6757 100644 --- a/compiler/rustc_mir/src/transform/check_consts/check.rs +++ b/compiler/rustc_mir/src/transform/check_consts/check.rs @@ -420,8 +420,8 @@ impl Checker<'mir, 'tcx> { ty::PredicateKind::ClosureKind(..) => { bug!("closure kind predicate on function: {:#?}", predicate) } - ty::PredicateKind::Subtype(_) => { - bug!("subtype predicate on function: {:#?}", predicate) + ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) => { + bug!("subtype/coerce predicate on function: {:#?}", predicate) } ty::PredicateKind::Trait(pred) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index 6fd7d29d777..464079656f9 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -369,10 +369,10 @@ impl DebugCounters { } return format!("({})", self.format_counter_kind(counter_kind)); } - return self.format_counter_kind(counter_kind).to_string(); + return self.format_counter_kind(counter_kind); } } - format!("#{}", operand.index().to_string()) + format!("#{}", operand.index()) } } diff --git a/compiler/rustc_mir/src/transform/remove_zsts.rs b/compiler/rustc_mir/src/transform/remove_zsts.rs index 40b1a8a2da9..03277b1b2e0 100644 --- a/compiler/rustc_mir/src/transform/remove_zsts.rs +++ b/compiler/rustc_mir/src/transform/remove_zsts.rs @@ -9,9 +9,6 @@ pub struct RemoveZsts; impl<'tcx> MirPass<'tcx> for RemoveZsts { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.mir_opt_level() < 3 { - return; - } let param_env = tcx.param_env(body.source.def_id()); let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); for block in basic_blocks.iter_mut() { diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 13cfc3695cc..1feb8b0d7a0 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -155,12 +155,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ascription: thir::Ascription { variance, user_ty, user_ty_span }, } => { // Apply the type ascription to the value at `match_pair.place`, which is the - candidate.ascriptions.push(Ascription { - span: user_ty_span, - user_ty, - source: match_pair.place.clone().into_place(self.tcx, self.typeck_results), - variance, - }); + if let Ok(place_resolved) = + match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { + candidate.ascriptions.push(Ascription { + span: user_ty_span, + user_ty, + source: place_resolved.into_place(self.tcx, self.typeck_results), + variance, + }); + } candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern)); @@ -173,15 +177,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } PatKind::Binding { name, mutability, mode, var, ty, ref subpattern, is_primary: _ } => { - candidate.bindings.push(Binding { - name, - mutability, - span: match_pair.pattern.span, - source: match_pair.place.clone().into_place(self.tcx, self.typeck_results), - var_id: var, - var_ty: ty, - binding_mode: mode, - }); + if let Ok(place_resolved) = + match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { + candidate.bindings.push(Binding { + name, + mutability, + span: match_pair.pattern.span, + source: place_resolved.into_place(self.tcx, self.typeck_results), + var_id: var, + var_ty: ty, + binding_mode: mode, + }); + } if let Some(subpattern) = subpattern.as_ref() { // this is the `x @ P` case; have to keep matching against `P` now diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 3cf8ae6efd9..88dd76e37c1 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -31,15 +31,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { suffix: &'pat [Pat<'tcx>], ) { let tcx = self.tcx; - let (min_length, exact_size) = match place - .clone() - .into_place(tcx, self.typeck_results) - .ty(&self.local_decls, tcx) - .ty - .kind() + let (min_length, exact_size) = if let Ok(place_resolved) = + place.clone().try_upvars_resolved(tcx, self.typeck_results) { - ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true), - _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), + match place_resolved + .into_place(tcx, self.typeck_results) + .ty(&self.local_decls, tcx) + .ty + .kind() + { + ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true), + _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), + } + } else { + ((prefix.len() + suffix.len()).try_into().unwrap(), false) }; match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 3df8ade2169..a683cb05e16 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -11,7 +11,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet}; +use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::intravisit::{self, DeepVisitor, NestedVisitorMap, Visitor}; use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind}; use rustc_middle::bug; @@ -354,9 +355,8 @@ trait VisibilityLike: Sized { // Returns an over-approximation (`skip_assoc_tys` = true) of visibility due to // associated types for which we can't determine visibility precisely. - fn of_impl(hir_id: hir::HirId, tcx: TyCtxt<'_>, access_levels: &AccessLevels) -> Self { + fn of_impl(def_id: LocalDefId, tcx: TyCtxt<'_>, access_levels: &AccessLevels) -> Self { let mut find = FindMin { tcx, access_levels, min: Self::MAX }; - let def_id = tcx.hir().local_def_id(hir_id); find.visit(tcx.type_of(def_id)); if let Some(trait_ref) = tcx.impl_trait_ref(def_id) { find.visit_trait(trait_ref); @@ -424,7 +424,7 @@ struct EmbargoVisitor<'tcx> { struct ReachEverythingInTheInterfaceVisitor<'a, 'tcx> { access_level: Option<AccessLevel>, - item_def_id: DefId, + item_def_id: LocalDefId, ev: &'a mut EmbargoVisitor<'tcx>, } @@ -448,12 +448,12 @@ impl EmbargoVisitor<'tcx> { fn reach( &mut self, - item_id: hir::HirId, + def_id: LocalDefId, access_level: Option<AccessLevel>, ) -> ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { ReachEverythingInTheInterfaceVisitor { access_level: cmp::min(access_level, Some(AccessLevel::Reachable)), - item_def_id: self.tcx.hir().local_def_id(item_id).to_def_id(), + item_def_id: def_id, ev: self, } } @@ -536,10 +536,10 @@ impl EmbargoVisitor<'tcx> { | hir::ItemKind::Union(ref struct_def, _) = item.kind { for field in struct_def.fields() { - let field_vis = - self.tcx.visibility(self.tcx.hir().local_def_id(field.hir_id)); + let def_id = self.tcx.hir().local_def_id(field.hir_id); + let field_vis = self.tcx.visibility(def_id); if field_vis.is_accessible_from(module.to_def_id(), self.tcx) { - self.reach(field.hir_id, level).ty(); + self.reach(def_id, level).ty(); } } } else { @@ -638,7 +638,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { let inherited_item_level = match item.kind { hir::ItemKind::Impl { .. } => { - Option::<AccessLevel>::of_impl(item.hir_id(), self.tcx, &self.access_levels) + Option::<AccessLevel>::of_impl(item.def_id, self.tcx, &self.access_levels) } // Foreign modules inherit level from parents. hir::ItemKind::ForeignMod { .. } => self.prev_level, @@ -750,7 +750,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { // reachable if they are returned via `impl Trait`, even from private functions. let exist_level = cmp::max(item_level, Some(AccessLevel::ReachableFromImplTrait)); - self.reach(item.hir_id(), exist_level).generics().predicates().ty(); + self.reach(item.def_id, exist_level).generics().predicates().ty(); } } // Visit everything. @@ -759,15 +759,15 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { | hir::ItemKind::Fn(..) | hir::ItemKind::TyAlias(..) => { if item_level.is_some() { - self.reach(item.hir_id(), item_level).generics().predicates().ty(); + self.reach(item.def_id, item_level).generics().predicates().ty(); } } hir::ItemKind::Trait(.., trait_item_refs) => { if item_level.is_some() { - self.reach(item.hir_id(), item_level).generics().predicates(); + self.reach(item.def_id, item_level).generics().predicates(); for trait_item_ref in trait_item_refs { - let mut reach = self.reach(trait_item_ref.id.hir_id(), item_level); + let mut reach = self.reach(trait_item_ref.id.def_id, item_level); reach.generics().predicates(); if trait_item_ref.kind == AssocItemKind::Type @@ -782,18 +782,18 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { } hir::ItemKind::TraitAlias(..) => { if item_level.is_some() { - self.reach(item.hir_id(), item_level).generics().predicates(); + self.reach(item.def_id, item_level).generics().predicates(); } } // Visit everything except for private impl items. hir::ItemKind::Impl(ref impl_) => { if item_level.is_some() { - self.reach(item.hir_id(), item_level).generics().predicates().ty().trait_ref(); + self.reach(item.def_id, item_level).generics().predicates().ty().trait_ref(); for impl_item_ref in impl_.items { let impl_item_level = self.get(impl_item_ref.id.def_id); if impl_item_level.is_some() { - self.reach(impl_item_ref.id.hir_id(), impl_item_level) + self.reach(impl_item_ref.id.def_id, impl_item_level) .generics() .predicates() .ty(); @@ -805,13 +805,14 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { // Visit everything, but enum variants have their own levels. hir::ItemKind::Enum(ref def, _) => { if item_level.is_some() { - self.reach(item.hir_id(), item_level).generics().predicates(); + self.reach(item.def_id, item_level).generics().predicates(); } for variant in def.variants { let variant_level = self.get(self.tcx.hir().local_def_id(variant.id)); if variant_level.is_some() { for field in variant.data.fields() { - self.reach(field.hir_id, variant_level).ty(); + self.reach(self.tcx.hir().local_def_id(field.hir_id), variant_level) + .ty(); } // Corner case: if the variant is reachable, but its // enum is not, make the enum reachable as well. @@ -824,7 +825,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { for foreign_item in items { let foreign_item_level = self.get(foreign_item.id.def_id); if foreign_item_level.is_some() { - self.reach(foreign_item.id.hir_id(), foreign_item_level) + self.reach(foreign_item.id.def_id, foreign_item_level) .generics() .predicates() .ty(); @@ -834,11 +835,12 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { // Visit everything except for private fields. hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { if item_level.is_some() { - self.reach(item.hir_id(), item_level).generics().predicates(); + self.reach(item.def_id, item_level).generics().predicates(); for field in struct_def.fields() { - let field_level = self.get(self.tcx.hir().local_def_id(field.hir_id)); + let def_id = self.tcx.hir().local_def_id(field.hir_id); + let field_level = self.get(def_id); if field_level.is_some() { - self.reach(field.hir_id, field_level).ty(); + self.reach(def_id, field_level).ty(); } } } @@ -992,7 +994,7 @@ impl DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { struct NamePrivacyVisitor<'tcx> { tcx: TyCtxt<'tcx>, maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, - current_item: Option<hir::HirId>, + current_item: LocalDefId, } impl<'tcx> NamePrivacyVisitor<'tcx> { @@ -1014,11 +1016,15 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { field: &'tcx ty::FieldDef, in_update_syntax: bool, ) { + if def.is_enum() { + return; + } + // definition of the field let ident = Ident::new(kw::Empty, use_ctxt); - let current_hir = self.current_item.unwrap(); - let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did, current_hir).1; - if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(self.current_item); + let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did, hir_id).1; + if !field.vis.is_accessible_from(def_id, self.tcx) { let label = if in_update_syntax { format!("field `{}` is private", field.ident) } else { @@ -1063,7 +1069,7 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - let orig_current_item = self.current_item.replace(item.hir_id()); + let orig_current_item = mem::replace(&mut self.current_item, item.def_id); intravisit::walk_item(self, item); self.current_item = orig_current_item; } @@ -1763,9 +1769,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { struct SearchInterfaceForPrivateItemsVisitor<'tcx> { tcx: TyCtxt<'tcx>, - item_id: hir::HirId, - item_def_id: DefId, - span: Span, + item_def_id: LocalDefId, /// The visitor checks that each component type is at least this visible. required_visibility: ty::Visibility, has_pub_restricted: bool, @@ -1820,8 +1824,8 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { if self.leaks_private_dep(def_id) { self.tcx.struct_span_lint_hir( lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES, - self.item_id, - self.span, + self.tcx.hir().local_def_id_to_hir_id(self.item_def_id), + self.tcx.def_span(self.item_def_id.to_def_id()), |lint| { lint.build(&format!( "{} `{}` from private dependency '{}' in public \ @@ -1856,15 +1860,16 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { } }; let make_msg = || format!("{} {} `{}` in public interface", vis_descr, kind, descr); + let span = self.tcx.def_span(self.item_def_id.to_def_id()); if self.has_pub_restricted || self.has_old_errors || self.in_assoc_ty { let mut err = if kind == "trait" { - struct_span_err!(self.tcx.sess, self.span, E0445, "{}", make_msg()) + struct_span_err!(self.tcx.sess, span, E0445, "{}", make_msg()) } else { - struct_span_err!(self.tcx.sess, self.span, E0446, "{}", make_msg()) + struct_span_err!(self.tcx.sess, span, E0446, "{}", make_msg()) }; let vis_span = self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)); - err.span_label(self.span, format!("can't leak {} {}", vis_descr, kind)); + err.span_label(span, format!("can't leak {} {}", vis_descr, kind)); err.span_label(vis_span, format!("`{}` declared as {}", descr, vis_descr)); err.emit(); } else { @@ -1872,7 +1877,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { self.tcx.struct_span_lint_hir( lint::builtin::PRIVATE_IN_PUBLIC, hir_id, - self.span, + span, |lint| lint.build(&format!("{} (error {})", make_msg(), err_code)).emit(), ); } @@ -1915,35 +1920,33 @@ impl DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> { struct PrivateItemsInPublicInterfacesVisitor<'tcx> { tcx: TyCtxt<'tcx>, has_pub_restricted: bool, - old_error_set_ancestry: HirIdSet, + old_error_set_ancestry: LocalDefIdSet, } impl<'tcx> PrivateItemsInPublicInterfacesVisitor<'tcx> { fn check( &self, - item_id: hir::HirId, + def_id: LocalDefId, required_visibility: ty::Visibility, ) -> SearchInterfaceForPrivateItemsVisitor<'tcx> { SearchInterfaceForPrivateItemsVisitor { tcx: self.tcx, - item_id, - item_def_id: self.tcx.hir().local_def_id(item_id).to_def_id(), - span: self.tcx.hir().span(item_id), + item_def_id: def_id, required_visibility, has_pub_restricted: self.has_pub_restricted, - has_old_errors: self.old_error_set_ancestry.contains(&item_id), + has_old_errors: self.old_error_set_ancestry.contains(&def_id), in_assoc_ty: false, } } fn check_assoc_item( &self, - hir_id: hir::HirId, + def_id: LocalDefId, assoc_item_kind: AssocItemKind, defaultness: hir::Defaultness, vis: ty::Visibility, ) { - let mut check = self.check(hir_id, vis); + let mut check = self.check(def_id, vis); let (check_ty, is_assoc_ty) = match assoc_item_kind { AssocItemKind::Const | AssocItemKind::Fn { .. } => (true, false), @@ -1982,38 +1985,38 @@ impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> { | hir::ItemKind::Static(..) | hir::ItemKind::Fn(..) | hir::ItemKind::TyAlias(..) => { - self.check(item.hir_id(), item_visibility).generics().predicates().ty(); + self.check(item.def_id, item_visibility).generics().predicates().ty(); } hir::ItemKind::OpaqueTy(..) => { // `ty()` for opaque types is the underlying type, // it's not a part of interface, so we skip it. - self.check(item.hir_id(), item_visibility).generics().bounds(); + self.check(item.def_id, item_visibility).generics().bounds(); } hir::ItemKind::Trait(.., trait_item_refs) => { - self.check(item.hir_id(), item_visibility).generics().predicates(); + self.check(item.def_id, item_visibility).generics().predicates(); for trait_item_ref in trait_item_refs { self.check_assoc_item( - trait_item_ref.id.hir_id(), + trait_item_ref.id.def_id, trait_item_ref.kind, trait_item_ref.defaultness, item_visibility, ); if let AssocItemKind::Type = trait_item_ref.kind { - self.check(trait_item_ref.id.hir_id(), item_visibility).bounds(); + self.check(trait_item_ref.id.def_id, item_visibility).bounds(); } } } hir::ItemKind::TraitAlias(..) => { - self.check(item.hir_id(), item_visibility).generics().predicates(); + self.check(item.def_id, item_visibility).generics().predicates(); } hir::ItemKind::Enum(ref def, _) => { - self.check(item.hir_id(), item_visibility).generics().predicates(); + self.check(item.def_id, item_visibility).generics().predicates(); for variant in def.variants { for field in variant.data.fields() { - self.check(field.hir_id, item_visibility).ty(); + self.check(self.tcx.hir().local_def_id(field.hir_id), item_visibility).ty(); } } } @@ -2021,16 +2024,17 @@ impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> { hir::ItemKind::ForeignMod { items, .. } => { for foreign_item in items { let vis = tcx.visibility(foreign_item.id.def_id); - self.check(foreign_item.id.hir_id(), vis).generics().predicates().ty(); + self.check(foreign_item.id.def_id, vis).generics().predicates().ty(); } } // Subitems of structs and unions have their own publicity. hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - self.check(item.hir_id(), item_visibility).generics().predicates(); + self.check(item.def_id, item_visibility).generics().predicates(); for field in struct_def.fields() { - let field_visibility = tcx.visibility(tcx.hir().local_def_id(field.hir_id)); - self.check(field.hir_id, min(item_visibility, field_visibility, tcx)).ty(); + let def_id = tcx.hir().local_def_id(field.hir_id); + let field_visibility = tcx.visibility(def_id); + self.check(def_id, min(item_visibility, field_visibility, tcx)).ty(); } } // An inherent impl is public when its type is public @@ -2038,8 +2042,8 @@ impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> { // A trait impl is public when both its type and its trait are public // Subitems of trait impls have inherited publicity. hir::ItemKind::Impl(ref impl_) => { - let impl_vis = ty::Visibility::of_impl(item.hir_id(), tcx, &Default::default()); - self.check(item.hir_id(), impl_vis).generics().predicates(); + let impl_vis = ty::Visibility::of_impl(item.def_id, tcx, &Default::default()); + self.check(item.def_id, impl_vis).generics().predicates(); for impl_item_ref in impl_.items { let impl_item_vis = if impl_.of_trait.is_none() { min(tcx.visibility(impl_item_ref.id.def_id), impl_vis, tcx) @@ -2047,7 +2051,7 @@ impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> { impl_vis }; self.check_assoc_item( - impl_item_ref.id.hir_id(), + impl_item_ref.id.def_id, impl_item_ref.kind, impl_item_ref.defaultness, impl_item_vis, @@ -2119,7 +2123,8 @@ fn visibility(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Visibility { fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { // Check privacy of names not checked in previous compilation stages. - let mut visitor = NamePrivacyVisitor { tcx, maybe_typeck_results: None, current_item: None }; + let mut visitor = + NamePrivacyVisitor { tcx, maybe_typeck_results: None, current_item: module_def_id }; let (module, span, hir_id) = tcx.hir().get_module(module_def_id); intravisit::walk_mod(&mut visitor, module, hir_id); @@ -2188,7 +2193,15 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) { } // Check for private types and traits in public interfaces. - let mut visitor = - PrivateItemsInPublicInterfacesVisitor { tcx, has_pub_restricted, old_error_set_ancestry }; + let mut visitor = PrivateItemsInPublicInterfacesVisitor { + tcx, + has_pub_restricted, + // Only definition IDs are ever searched in `old_error_set_ancestry`, + // so we can filter away all non-definition IDs at this point. + old_error_set_ancestry: old_error_set_ancestry + .into_iter() + .filter_map(|hir_id| tcx.hir().opt_local_def_id(hir_id)) + .collect(), + }; krate.visit_all_item_likes(&mut DeepVisitor::new(&mut visitor)); } diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs index ace7cffd16d..38ab26d66ac 100644 --- a/compiler/rustc_query_impl/src/keys.rs +++ b/compiler/rustc_query_impl/src/keys.rs @@ -332,6 +332,16 @@ impl<'tcx> Key for Ty<'tcx> { } } +impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) { + #[inline(always)] + fn query_crate_is_local(&self) -> bool { + true + } + fn default_span(&self, _: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for &'tcx ty::List<ty::Predicate<'tcx>> { #[inline(always)] fn query_crate_is_local(&self) -> bool { diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 9f4f2b82f60..760b7469961 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -63,8 +63,7 @@ impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> { // We have information about whether `use` (import) items are actually // used now. If an import is not used at all, we signal a lint error. fn check_import(&mut self, id: ast::NodeId) { - let mut used = false; - self.r.per_ns(|this, ns| used |= this.used_imports.contains(&(id, ns))); + let used = self.r.used_imports.contains(&id); let def_id = self.r.local_def_id(id); if !used { if self.r.maybe_unused_trait_imports.contains(&def_id) { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 0a5da653fab..3cf04268756 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -950,9 +950,7 @@ impl<'a> Resolver<'a> { self.add_typo_suggestion(err, suggestion, ident.span); let import_suggestions = - self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, |res| { - matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _)) - }); + self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); show_candidates(err, None, &import_suggestions, false, true); if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index acfa389fed5..dfb6d89a0d1 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -303,7 +303,7 @@ impl<'a> Resolver<'a> { if self.last_import_segment && check_usable(self, binding).is_err() { Err((Determined, Weak::No)) } else { - self.record_use(ident, ns, binding, restricted_shadowing); + self.record_use(ident, binding, restricted_shadowing); if let Some(shadowed_glob) = resolution.shadowed_glob { // Forbid expanded shadowing to avoid time travel. @@ -609,9 +609,9 @@ impl<'a> Resolver<'a> { self.per_ns(|this, ns| { let key = this.new_key(target, ns); let _ = this.try_define(import.parent_scope.module, key, dummy_binding); - // Consider erroneous imports used to avoid duplicate diagnostics. - this.record_use(target, ns, dummy_binding, false); }); + // Consider erroneous imports used to avoid duplicate diagnostics. + self.record_use(target, dummy_binding, false); } } } @@ -709,7 +709,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } } else if is_indeterminate { // Consider erroneous imports used to avoid duplicate diagnostics. - self.r.used_imports.insert((import.id, TypeNS)); + self.r.used_imports.insert(import.id); let path = import_path_to_string( &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(), &import.kind, @@ -902,7 +902,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { import.vis.set(orig_vis); if let PathResult::Failed { .. } | PathResult::NonModule(..) = path_res { // Consider erroneous imports used to avoid duplicate diagnostics. - self.r.used_imports.insert((import.id, TypeNS)); + self.r.used_imports.insert(import.id); } let module = match path_res { PathResult::Module(module) => { @@ -1043,7 +1043,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> { { this.record_use( ident, - ns, target_binding, import.module_path.is_empty(), ); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 00d291946df..0b552aa07f5 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1738,7 +1738,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // whether they can be shadowed by fresh bindings or not, so force an error. // issues/33118#issuecomment-233962221 (see below) still applies here, // but we have to ignore it for backward compatibility. - self.r.record_use(ident, ValueNS, binding, false); + self.r.record_use(ident, binding, false); return None; } LexicalScopeBinding::Item(binding) => (binding.res(), Some(binding)), @@ -1753,7 +1753,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ) if is_syntactic_ambiguity => { // Disambiguate in favor of a unit struct/variant or constant pattern. if let Some(binding) = binding { - self.r.record_use(ident, ValueNS, binding, false); + self.r.record_use(ident, binding, false); } Some(res) } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 7114fd33188..465007507da 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -942,7 +942,7 @@ pub struct Resolver<'a> { glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>, /// Visibilities in "lowered" form, for all entities that have them. visibilities: FxHashMap<LocalDefId, ty::Visibility>, - used_imports: FxHashSet<(NodeId, Namespace)>, + used_imports: FxHashSet<NodeId>, maybe_unused_trait_imports: FxHashSet<LocalDefId>, maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, @@ -1656,7 +1656,6 @@ impl<'a> Resolver<'a> { fn record_use( &mut self, ident: Ident, - ns: Namespace, used_binding: &'a NameBinding<'a>, is_lexical_scope: bool, ) { @@ -1684,9 +1683,9 @@ impl<'a> Resolver<'a> { } used.set(true); import.used.set(true); - self.used_imports.insert((import.id, ns)); + self.used_imports.insert(import.id); self.add_to_glob_map(&import, ident); - self.record_use(ident, ns, binding, false); + self.record_use(ident, binding, false); } } @@ -3241,7 +3240,7 @@ impl<'a> Resolver<'a> { self.extern_prelude.get(&ident.normalize_to_macros_2_0()).cloned().and_then(|entry| { if let Some(binding) = entry.extern_crate_item { if !speculative && entry.introduced_by_item { - self.record_use(ident, TypeNS, binding, false); + self.record_use(ident, binding, false); } Some(binding) } else { @@ -3428,7 +3427,7 @@ impl<'a> Resolver<'a> { let is_import = name_binding.is_import(); let span = name_binding.span; if let Res::Def(DefKind::Fn, _) = res { - self.record_use(ident, ValueNS, name_binding, false); + self.record_use(ident, name_binding, false); } self.main_def = Some(MainDefinition { res, is_import, span }); } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index b2a8aa0cecc..7f86f891c44 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1090,7 +1090,7 @@ impl<'a> Resolver<'a> { ) { Ok(binding) => { let initial_res = initial_binding.map(|initial_binding| { - self.record_use(ident, MacroNS, initial_binding, false); + self.record_use(ident, initial_binding, false); initial_binding.res() }); let res = binding.res(); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 1c95cc91208..9e127577b61 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -157,7 +157,7 @@ scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals); // FIXME: We should use this enum or something like it to get rid of the // use of magic `/rust/1.x/...` paths across the board. #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd)] -#[derive(HashStable_Generic, Decodable)] +#[derive(Decodable)] pub enum RealFileName { LocalPath(PathBuf), /// For remapped paths (namely paths into libstd that have been mapped @@ -269,7 +269,7 @@ impl RealFileName { /// Differentiates between real files and common virtual files. #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)] -#[derive(HashStable_Generic, Decodable, Encodable)] +#[derive(Decodable, Encodable)] pub enum FileName { Real(RealFileName), /// Call to `quote!`. diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs index e5805d9e691..6468419fce7 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs @@ -2,8 +2,15 @@ use super::apple_sdk_base::{opts, Arch}; use crate::spec::{FramePointer, Target, TargetOptions}; pub fn target() -> Target { + // Clang automatically chooses a more specific target based on + // IPHONEOS_DEPLOYMENT_TARGET. + // This is required for the target to pick the right + // MACH-O commands, so we do too. + let arch = "arm64"; + let llvm_target = super::apple_base::ios_llvm_target(arch); + Target { - llvm_target: "arm64-apple-ios".to_string(), + llvm_target, pointer_width: 64, data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs index 09ea7d33cfd..0caecd2987b 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs @@ -1,4 +1,4 @@ -use crate::spec::{Target, TargetOptions}; +use crate::spec::{SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { Target { @@ -6,6 +6,12 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), - options: TargetOptions { max_atomic_width: Some(128), ..super::freebsd_base::opts() }, + options: TargetOptions { + max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::MEMORY + | SanitizerSet::THREAD, + ..super::freebsd_base::opts() + }, } } diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index 0c8a89210ff..cff0b3651e1 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -91,6 +91,11 @@ fn ios_deployment_target() -> (u32, u32) { deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((7, 0)) } +pub fn ios_llvm_target(arch: &str) -> String { + let (major, minor) = ios_deployment_target(); + format!("{}-apple-ios{}.{}.0", arch, major, minor) +} + pub fn ios_sim_llvm_target(arch: &str) -> String { let (major, minor) = ios_deployment_target(); format!("{}-apple-ios{}.{}.0-simulator", arch, major, minor) diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs index 559a1a40868..f10d4d49bb9 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs @@ -14,7 +14,7 @@ pub fn target() -> Target { Target { llvm_target: "powerpc64-unknown-linux-gnu".to_string(), pointer_width: 64, - data_layout: "E-m:e-i64:64-n32:64-v256:256:256-v512:512:512".to_string(), + data_layout: "E-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".to_string(), arch: "powerpc64".to_string(), options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs index f1190b159ab..611621727bd 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs @@ -10,7 +10,7 @@ pub fn target() -> Target { Target { llvm_target: "powerpc64-unknown-linux-musl".to_string(), pointer_width: 64, - data_layout: "E-m:e-i64:64-n32:64-v256:256:256-v512:512:512".to_string(), + data_layout: "E-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".to_string(), arch: "powerpc64".to_string(), options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } diff --git a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs index 3ebc5469e0a..9c63997ce2f 100644 --- a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs @@ -10,7 +10,7 @@ pub fn target() -> Target { Target { llvm_target: "powerpc64-unknown-linux-gnu".to_string(), pointer_width: 64, - data_layout: "E-m:e-i64:64-n32:64-v256:256:256-v512:512:512".to_string(), + data_layout: "E-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".to_string(), arch: "powerpc64".to_string(), options: TargetOptions { endian: Endian::Big, ..base }, } diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs index 76f70e474f0..f645eceadfe 100644 --- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs @@ -9,7 +9,7 @@ pub fn target() -> Target { Target { llvm_target: "powerpc64le-unknown-linux-gnu".to_string(), pointer_width: 64, - data_layout: "e-m:e-i64:64-n32:64-v256:256:256-v512:512:512".to_string(), + data_layout: "e-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".to_string(), arch: "powerpc64".to_string(), options: TargetOptions { mcount: "_mcount".to_string(), ..base }, } diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs index 42c49103b3b..934371fb221 100644 --- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs @@ -9,7 +9,7 @@ pub fn target() -> Target { Target { llvm_target: "powerpc64le-unknown-linux-musl".to_string(), pointer_width: 64, - data_layout: "e-m:e-i64:64-n32:64-v256:256:256-v512:512:512".to_string(), + data_layout: "e-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512".to_string(), arch: "powerpc64".to_string(), options: TargetOptions { mcount: "_mcount".to_string(), ..base }, } diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index 302139395d3..86b1a755233 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -43,7 +43,7 @@ pub fn target() -> Target { Target { llvm_target: "wasm32-unknown-emscripten".to_string(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-f128:64-n32:64-S128-ni:1:10:20".to_string(), arch: "wasm32".to_string(), options: opts, } diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs index 834c4dbfc05..134c6803b15 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs @@ -54,7 +54,7 @@ pub fn target() -> Target { Target { llvm_target: "wasm32-unknown-unknown".to_string(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20".to_string(), arch: "wasm32".to_string(), options, } diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index a6b12d2ee8f..2dab206dc76 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -109,7 +109,7 @@ pub fn target() -> Target { Target { llvm_target: "wasm32-wasi".to_string(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20".to_string(), arch: "wasm32".to_string(), options, } diff --git a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs index 8bfb229d77f..fb6526c0e72 100644 --- a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs @@ -32,7 +32,7 @@ pub fn target() -> Target { Target { llvm_target: "wasm64-unknown-unknown".to_string(), pointer_width: 64, - data_layout: "e-m:e-p:64:64-i64:64-n32:64-S128".to_string(), + data_layout: "e-m:e-p:64:64-i64:64-n32:64-S128-ni:1:10:20".to_string(), arch: "wasm64".to_string(), options, } diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 1a195ce18ec..e6686b8cc70 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -1119,6 +1119,7 @@ crate fn required_region_bounds( ty::PredicateKind::Projection(..) | ty::PredicateKind::Trait(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 2f91666ca64..2c09c551b65 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -565,6 +565,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) } + ty::PredicateKind::Coerce(predicate) => { + // Errors for Coerce predicates show up as + // `FulfillmentErrorCode::CodeSubtypeError`, + // not selection error. + span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) + } + ty::PredicateKind::RegionOutlives(predicate) => { let predicate = bound_predicate.rebind(predicate); let predicate = self.resolve_vars_if_possible(predicate); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 5499993db63..003642ff259 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2072,6 +2072,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::SizedYieldType => { err.note("the yield type of a generator must have a statically known size"); } + ObligationCauseCode::SizedBoxType => { + err.note("the type of a box expression must have a statically known size"); + } ObligationCauseCode::AssignmentLhsSized => { err.note("the left-hand-side of an assignment must have a statically known size"); } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 7048f0dbedc..4b94deff825 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -402,6 +402,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { | ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::Coerce(_) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) => { let pred = infcx.replace_bound_vars_with_placeholders(binder); @@ -517,6 +518,31 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } + ty::PredicateKind::Coerce(coerce) => { + match self.selcx.infcx().coerce_predicate( + &obligation.cause, + obligation.param_env, + Binder::dummy(coerce), + ) { + None => { + // None means that both are unresolved. + pending_obligation.stalled_on = vec![ + TyOrConstInferVar::maybe_from_ty(coerce.a).unwrap(), + TyOrConstInferVar::maybe_from_ty(coerce.b).unwrap(), + ]; + ProcessResult::Unchanged + } + Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Some(Err(err)) => { + let expected_found = ExpectedFound::new(false, coerce.a, coerce.b); + ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( + expected_found, + err, + )) + } + } + } + ty::PredicateKind::ConstEvaluatable(def_id, substs) => { match const_evaluatable::is_const_evaluatable( self.selcx.infcx(), @@ -552,11 +578,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val, c2.val) { - if self - .selcx - .tcx() - .try_unify_abstract_consts(((a.def, a.substs), (b.def, b.substs))) - { + if infcx.try_unify_abstract_consts(a, b) { return ProcessResult::Changed(vec![]); } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 585fcf795b7..477d29f1a4f 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -28,6 +28,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::{ @@ -759,48 +760,38 @@ fn vtable_trait_first_method_offset<'tcx>( pub fn vtable_trait_upcasting_coercion_new_vptr_slot( tcx: TyCtxt<'tcx>, key: ( - ty::PolyTraitRef<'tcx>, // trait owning vtable - ty::PolyTraitRef<'tcx>, // super trait ref + Ty<'tcx>, // trait object type whose trait owning vtable + Ty<'tcx>, // trait object for supertrait ), ) -> Option<usize> { - let (trait_owning_vtable, super_trait_ref) = key; - let super_trait_did = super_trait_ref.def_id(); - // FIXME: take substsref part into account here after upcasting coercion allows the same def_id occur - // multiple times. + let (source, target) = key; + assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer()); + assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer()); - let vtable_segment_callback = { - let mut vptr_offset = 0; - move |segment| { - match segment { - VtblSegment::MetadataDSA => { - vptr_offset += COMMON_VTABLE_ENTRIES.len(); - } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - vptr_offset += util::count_own_vtable_entries(tcx, trait_ref); - if trait_ref.def_id() == super_trait_did { - if emit_vptr { - return ControlFlow::Break(Some(vptr_offset)); - } else { - return ControlFlow::Break(None); - } - } + // this has been typecked-before, so diagnostics is not really needed. + let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None); - if emit_vptr { - vptr_offset += 1; - } - } - } - ControlFlow::Continue(()) - } + let trait_ref = ty::TraitRef { + def_id: unsize_trait_did, + substs: tcx.mk_substs_trait(source, &[target.into()]), }; + let obligation = Obligation::new( + ObligationCause::dummy(), + ty::ParamEnv::reveal_all(), + ty::Binder::dummy(ty::TraitPredicate { trait_ref, constness: hir::Constness::NotConst }), + ); - if let Some(vptr_offset) = - prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback) - { - vptr_offset - } else { - bug!("Failed to find info for expected trait in vtable"); - } + let implsrc = tcx.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + selcx.select(&obligation).unwrap() + }); + + let implsrc_traitcasting = match implsrc { + Some(ImplSource::TraitUpcasting(data)) => data, + _ => bug!(), + }; + + implsrc_traitcasting.vtable_vptr_slot } pub fn provide(providers: &mut ty::query::Providers) { diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 04bc689d511..02b43de0d16 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -308,6 +308,7 @@ fn predicate_references_self( | ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, @@ -336,6 +337,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } ty::PredicateKind::Projection(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index d1ab9fa025e..f75f7b887a5 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1483,7 +1483,9 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // why we special case object types. false } - super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) => { + super::ImplSource::AutoImpl(..) + | super::ImplSource::Builtin(..) + | super::ImplSource::TraitUpcasting(_) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -1554,6 +1556,7 @@ fn confirm_select_candidate<'cx, 'tcx>( | super::ImplSource::AutoImpl(..) | super::ImplSource::Param(..) | super::ImplSource::Builtin(..) + | super::ImplSource::TraitUpcasting(_) | super::ImplSource::TraitAlias(..) => { // we don't create Select candidates with this kind of resolution span_bug!( diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 4312cc94682..e18828fec3f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -690,19 +690,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?source, ?target, "assemble_candidates_for_unsizing"); - let may_apply = match (source.kind(), target.kind()) { + match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { - // See `confirm_builtin_unsize_candidate` for more info. + // Upcast coercions permit several things: + // + // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` + // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` + // 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar` + // + // Note that neither of the first two of these changes requires any + // change at runtime. The third needs to change pointer metadata at runtime. + // + // We always perform upcasting coercions when we can because of reason + // #2 (region bounds). let auto_traits_compatible = data_b .auto_traits() // All of a's auto traits need to be in b's auto traits. .all(|b| data_a.auto_traits().any(|a| a == b)); - auto_traits_compatible + if auto_traits_compatible { + let principal_def_id_a = data_a.principal_def_id(); + let principal_def_id_b = data_b.principal_def_id(); + if principal_def_id_a == principal_def_id_b { + // no cyclic + candidates.vec.push(BuiltinUnsizeCandidate); + } else if principal_def_id_a.is_some() && principal_def_id_b.is_some() { + // not casual unsizing, now check whether this is trait upcasting coercion. + let principal_a = data_a.principal().unwrap(); + let target_trait_did = principal_def_id_b.unwrap(); + let source_trait_ref = principal_a.with_self_ty(self.tcx(), source); + for (idx, upcast_trait_ref) in + util::supertraits(self.tcx(), source_trait_ref).enumerate() + { + if upcast_trait_ref.def_id() == target_trait_did { + candidates.vec.push(TraitUpcastingUnsizeCandidate(idx)); + } + } + } + } } // `T` -> `Trait` - (_, &ty::Dynamic(..)) => true, + (_, &ty::Dynamic(..)) => { + candidates.vec.push(BuiltinUnsizeCandidate); + } // Ambiguous handling is below `T` -> `Trait`, because inference // variables can still implement `Unsize<Trait>` and nested @@ -710,26 +741,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => { debug!("assemble_candidates_for_unsizing: ambiguous"); candidates.ambiguous = true; - false } // `[T; n]` -> `[T]` - (&ty::Array(..), &ty::Slice(_)) => true, + (&ty::Array(..), &ty::Slice(_)) => { + candidates.vec.push(BuiltinUnsizeCandidate); + } // `Struct<T>` -> `Struct<U>` (&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => { - def_id_a == def_id_b + if def_id_a == def_id_b { + candidates.vec.push(BuiltinUnsizeCandidate); + } } // `(.., T)` -> `(.., U)` - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(), + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { + if tys_a.len() == tys_b.len() { + candidates.vec.push(BuiltinUnsizeCandidate); + } + } - _ => false, + _ => {} }; - - if may_apply { - candidates.vec.push(BuiltinUnsizeCandidate); - } } fn assemble_candidates_for_trait_alias( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 0c2099593a2..0f2f5357eb7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -26,12 +26,13 @@ use crate::traits::Normalized; use crate::traits::OutputTypeParameterMismatch; use crate::traits::Selection; use crate::traits::TraitNotObjectSafe; +use crate::traits::VtblSegment; use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation}; use crate::traits::{ ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData, - ImplSourceUserDefinedData, + ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, }; use crate::traits::{ObjectCastObligation, PredicateObligation, TraitObligation}; use crate::traits::{Obligation, ObligationCause}; @@ -42,6 +43,7 @@ use super::SelectionCandidate::{self, *}; use super::SelectionContext; use std::iter; +use std::ops::ControlFlow; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] @@ -118,6 +120,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let data = self.confirm_builtin_unsize_candidate(obligation)?; Ok(ImplSource::Builtin(data)) } + + TraitUpcastingUnsizeCandidate(idx) => { + let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; + Ok(ImplSource::TraitUpcasting(data)) + } } } @@ -685,10 +692,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) } - fn confirm_builtin_unsize_candidate( + fn confirm_trait_upcasting_unsize_candidate( &mut self, obligation: &TraitObligation<'tcx>, - ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + idx: usize, + ) -> Result<ImplSourceTraitUpcastingData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> + { let tcx = self.tcx(); // `assemble_candidates_for_unsizing` should ensure there are no late-bound @@ -697,62 +706,123 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); let target = self.infcx.shallow_resolve(target); - debug!(?source, ?target, "confirm_builtin_unsize_candidate"); + debug!(?source, ?target, "confirm_trait_upcasting_unsize_candidate"); let mut nested = vec![]; + let source_trait_ref; + let upcast_trait_ref; match (source.kind(), target.kind()) { - // Trait+Kx+'a -> Trait+Ky+'b (upcasts). + // TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion). (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { - // Upcast coercions permit several things: - // - // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` - // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` - // 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar` - // - // Note that neither of the first two of these changes requires any - // change at runtime. The third needs to change pointer metadata at runtime. - // - // We always perform upcasting coercions when we can because of reason - // #2 (region bounds). - + // See `assemble_candidates_for_unsizing` for more info. // We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`. + let principal_a = data_a.principal().unwrap(); + source_trait_ref = principal_a.with_self_ty(tcx, source); + upcast_trait_ref = util::supertraits(tcx, source_trait_ref).nth(idx).unwrap(); + assert_eq!(data_b.principal_def_id(), Some(upcast_trait_ref.def_id())); + let existential_predicate = upcast_trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( + tcx, trait_ref, + )) + }); + let iter = Some(existential_predicate) + .into_iter() + .chain( + data_a + .projection_bounds() + .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)), + ) + .chain( + data_b + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let existential_predicates = tcx.mk_poly_existential_predicates(iter); + let source_trait = tcx.mk_dynamic(existential_predicates, r_b); - let principal_a = data_a.principal(); - let principal_def_id_b = data_b.principal_def_id(); - - let existential_predicate = if let Some(principal_a) = principal_a { - let source_trait_ref = principal_a.with_self_ty(tcx, source); - let target_trait_did = principal_def_id_b.ok_or_else(|| Unimplemented)?; - let upcast_idx = util::supertraits(tcx, source_trait_ref) - .position(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did) - .ok_or_else(|| Unimplemented)?; - // FIXME(crlf0710): This is less than ideal, for example, - // if the trait is defined as `trait Foo: Bar<u32> + Bar<i32>`, - // the coercion from Box<Foo> to Box<dyn Bar<_>> is actually ambiguous. - // We currently make this coercion fail for now. - // - // see #65991 for more information. - if util::supertraits(tcx, source_trait_ref) - .skip(upcast_idx + 1) - .any(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did) - { - return Err(Unimplemented); + // Require that the traits involved in this upcast are **equal**; + // only the **lifetime bound** is changed. + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .sup(target, source_trait) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // Register one obligation for 'a: 'b. + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObjectCastObligation(target), + ); + let outlives = ty::OutlivesPredicate(r_a, r_b); + nested.push(Obligation::with_depth( + cause, + obligation.recursion_depth + 1, + obligation.param_env, + obligation.predicate.rebind(outlives).to_predicate(tcx), + )); + } + _ => bug!(), + }; + + let vtable_segment_callback = { + let mut vptr_offset = 0; + move |segment| { + match segment { + VtblSegment::MetadataDSA => { + vptr_offset += ty::COMMON_VTABLE_ENTRIES.len(); } - let target_trait_ref = - util::supertraits(tcx, source_trait_ref).nth(upcast_idx).unwrap(); - let existential_predicate = target_trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( - tcx, trait_ref, - )) - }); - Some(existential_predicate) - } else if principal_def_id_b.is_none() { - None - } else { - return Err(Unimplemented); - }; + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + vptr_offset += util::count_own_vtable_entries(tcx, trait_ref); + if trait_ref == upcast_trait_ref { + if emit_vptr { + return ControlFlow::Break(Some(vptr_offset)); + } else { + return ControlFlow::Break(None); + } + } + + if emit_vptr { + vptr_offset += 1; + } + } + } + ControlFlow::Continue(()) + } + }; + + let vtable_vptr_slot = + super::super::prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback) + .unwrap(); - let iter = existential_predicate + Ok(ImplSourceTraitUpcastingData { upcast_trait_ref, vtable_vptr_slot, nested }) + } + + fn confirm_builtin_unsize_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + let tcx = self.tcx(); + + // `assemble_candidates_for_unsizing` should ensure there are no late-bound + // regions here. See the comment there for more details. + let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); + let target = self.infcx.shallow_resolve(target); + + debug!(?source, ?target, "confirm_builtin_unsize_candidate"); + + let mut nested = vec![]; + match (source.kind(), target.kind()) { + // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). + (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { + // See `assemble_candidates_for_unsizing` for more info. + // We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`. + let iter = data_a + .principal() + .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) .into_iter() .chain( data_a diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 38dbacbf2ae..c150b222266 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -512,6 +512,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + ty::PredicateKind::Coerce(p) => { + let p = bound_predicate.rebind(p); + // Does this code ever run? + match self.infcx.coerce_predicate(&obligation.cause, obligation.param_env, p) { + Some(Ok(InferOk { mut obligations, .. })) => { + self.add_depth(obligations.iter_mut(), obligation.recursion_depth); + self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ) + } + Some(Err(_)) => Ok(EvaluatedToErr), + None => Ok(EvaluatedToAmbig), + } + } + ty::PredicateKind::WellFormed(arg) => match wf::obligations( self.infcx, obligation.param_env, @@ -608,10 +624,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val, c2.val) { - if self - .tcx() - .try_unify_abstract_consts(((a.def, a.substs), (b.def, b.substs))) - { + if self.infcx.try_unify_abstract_consts(a, b) { return Ok(EvaluatedToOk); } } @@ -1500,6 +1513,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate(..) | ObjectCandidate(_) @@ -1517,6 +1531,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate(..), ParamCandidate(ref cand), @@ -1546,6 +1561,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate(..), ) => true, @@ -1557,6 +1573,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate(..), ObjectCandidate(_) | ProjectionCandidate(_), @@ -1566,12 +1583,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // See if we can toss out `victim` based on specialization. // This requires us to know *for sure* that the `other` impl applies // i.e., `EvaluatedToOk`. + // + // FIXME(@lcnr): Using `modulo_regions` here seems kind of scary + // to me but is required for `std` to compile, so I didn't change it + // for now. + let tcx = self.tcx(); if other.evaluation.must_apply_modulo_regions() { - let tcx = self.tcx(); if tcx.specializes((other_def, victim_def)) { return true; } - return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { + } + + if other.evaluation.must_apply_considering_regions() { + match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { Some(ty::ImplOverlapKind::Permitted { marker: true }) => { // Subtle: If the predicate we are evaluating has inference // variables, do *not* allow discarding candidates due to @@ -1616,7 +1640,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } Some(_) => true, None => false, - }; + } } else { false } @@ -1630,6 +1654,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate(..), ImplCandidate(_) @@ -1638,6 +1663,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate(..), ) => false, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 9c9664d7b5e..afef784b4c6 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -128,6 +128,10 @@ pub fn predicate_obligations<'a, 'tcx>( wf.compute(a.into()); wf.compute(b.into()); } + ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { + wf.compute(a.into()); + wf.compute(b.into()); + } ty::PredicateKind::ConstEvaluatable(def, substs) => { let obligations = wf.nominal_obligations(def.did, substs); wf.out.extend(obligations); diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 974755e9e26..330fd497fa1 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -109,6 +109,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<' | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) => bug!("unexpected predicate {}", predicate), }; @@ -193,6 +194,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi // some of these in terms of chalk operations. ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) => { chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) @@ -592,6 +594,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<' ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => { @@ -719,6 +722,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_solve::rust_ir::QuantifiedInlineBound<Ru | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => { diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 90ba90259c3..1d10d068490 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -99,6 +99,7 @@ fn compute_implied_outlives_bounds<'tcx>( Some(pred) => match pred { ty::PredicateKind::Trait(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Projection(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::ObjectSafe(..) diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 5ad0684fe6e..61ab5e28b67 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -65,6 +65,7 @@ fn not_outlives_predicate(p: &ty::Predicate<'tcx>) -> bool { | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => true, diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 469ac04e545..4c03abb38ca 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -381,7 +381,8 @@ fn resolve_associated_item<'tcx>( | traits::ImplSource::Param(..) | traits::ImplSource::TraitAlias(..) | traits::ImplSource::DiscriminantKind(..) - | traits::ImplSource::Pointee(..) => None, + | traits::ImplSource::Pointee(..) + | traits::ImplSource::TraitUpcasting(_) => None, }) } diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 8eb51b977ed..aae9518ad51 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -1413,15 +1413,17 @@ fn check_enum<'tcx>( Some(ref expr) => tcx.hir().span(expr.hir_id), None => v.span, }; + let display_discr = display_discriminant_value(tcx, v, discr.val); + let display_discr_i = display_discriminant_value(tcx, variant_i, disr_vals[i].val); struct_span_err!( tcx.sess, span, E0081, "discriminant value `{}` already exists", - disr_vals[i] + discr.val, ) - .span_label(i_span, format!("first use of `{}`", disr_vals[i])) - .span_label(span, format!("enum already has `{}`", disr_vals[i])) + .span_label(i_span, format!("first use of {}", display_discr_i)) + .span_label(span, format!("enum already has {}", display_discr)) .emit(); } disr_vals.push(discr); @@ -1431,6 +1433,25 @@ fn check_enum<'tcx>( check_transparent(tcx, sp, def); } +/// Format an enum discriminant value for use in a diagnostic message. +fn display_discriminant_value<'tcx>( + tcx: TyCtxt<'tcx>, + variant: &hir::Variant<'_>, + evaluated: u128, +) -> String { + if let Some(expr) = &variant.disr_expr { + let body = &tcx.hir().body(expr.body).value; + if let hir::ExprKind::Lit(lit) = &body.kind { + if let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node { + if evaluated != *lit_value { + return format!("`{}` (overflowed from `{}`)", evaluated, lit_value); + } + } + } + } + format!("`{}`", evaluated) +} + pub(super) fn check_type_params_are_used<'tcx>( tcx: TyCtxt<'tcx>, generics: &ty::Generics, diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 208eb27c844..0b4df8e6d3c 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -42,6 +42,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; +use rustc_infer::traits::Obligation; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, @@ -50,7 +51,7 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_middle::ty::{self, ToPredicate, Ty, TypeAndMut}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{self, BytePos, Span}; @@ -146,7 +147,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + // First, remove any resolved type variables (at the top level, at least): let a = self.shallow_resolve(a); + let b = self.shallow_resolve(b); debug!("Coerce.tys({:?} => {:?})", a, b); // Just ignore error types. @@ -154,6 +157,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return success(vec![], self.fcx.tcx.ty_error(), vec![]); } + // Coercing from `!` to any type is allowed: if a.is_never() { // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound // type variable, we want `?T` to fallback to `!` if not @@ -162,20 +166,26 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // let _: Option<?T> = Some({ return; }); // // here, we would coerce from `!` to `?T`. - let b = self.shallow_resolve(b); - return if self.shallow_resolve(b).is_ty_var() { + return if b.is_ty_var() { // Micro-optimization: no need for this if `b` is // already resolved in some way. let diverging_ty = self.next_diverging_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::AdjustmentType, span: self.cause.span, }); - self.unify_and(&b, &diverging_ty, simple(Adjust::NeverToAny)) + self.coerce_from_inference_variable(diverging_ty, b, simple(Adjust::NeverToAny)) } else { success(simple(Adjust::NeverToAny)(b), b, vec![]) }; } + // Coercing *from* an unresolved inference variable means that + // we have no information about the source type. This will always + // ultimately fall back to some form of subtyping. + if a.is_ty_var() { + return self.coerce_from_inference_variable(a, b, identity); + } + // Consider coercing the subtype to a DST // // NOTE: this is wrapped in a `commit_if_ok` because it creates @@ -196,9 +206,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("coerce: unsize failed"); // Examine the supertype and consider auto-borrowing. - // - // Note: does not attempt to resolve type variables we encounter. - // See above for details. match *b.kind() { ty::RawPtr(mt_b) => { return self.coerce_unsafe_ptr(a, b, mt_b.mutbl); @@ -236,6 +243,58 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } + /// Coercing *from* an inference variable. In this case, we have no information + /// about the source type, so we can't really do a true coercion and we always + /// fall back to subtyping (`unify_and`). + fn coerce_from_inference_variable( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + make_adjustments: impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, + ) -> CoerceResult<'tcx> { + debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); + assert!(a.is_ty_var() && self.infcx.shallow_resolve(a) == a); + assert!(self.infcx.shallow_resolve(b) == b); + + if b.is_ty_var() { + // Two unresolved type variables: create a `Coerce` predicate. + let target_ty = if self.use_lub { + self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::LatticeVariable, + span: self.cause.span, + }) + } else { + b + }; + + let mut obligations = Vec::with_capacity(2); + for &source_ty in &[a, b] { + if source_ty != target_ty { + obligations.push(Obligation::new( + self.cause.clone(), + self.param_env, + ty::PredicateKind::Coerce(ty::CoercePredicate { + a: source_ty, + b: target_ty, + }) + .to_predicate(self.tcx()), + )); + } + } + + debug!( + "coerce_from_inference_variable: two inference variables, target_ty={:?}, obligations={:?}", + target_ty, obligations + ); + let adjustments = make_adjustments(target_ty); + InferResult::Ok(InferOk { value: (adjustments, target_ty), obligations }) + } else { + // One unresolved type variable: just apply subtyping, we may be able + // to do something useful. + self.unify_and(a, b, make_adjustments) + } + } + /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. /// To match `A` with `B`, autoderef will be performed, /// calling `deref`/`deref_mut` where necessary. diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 316a097556a..8d5bf98be99 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -708,11 +708,7 @@ fn compare_number_of_method_arguments<'tcx>( Some(if pos == 0 { arg.span } else { - Span::new( - trait_m_sig.decl.inputs[0].span.lo(), - arg.span.hi(), - arg.span.ctxt(), - ) + arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo()) }) } else { trait_item_span @@ -731,11 +727,7 @@ fn compare_number_of_method_arguments<'tcx>( if pos == 0 { arg.span } else { - Span::new( - impl_m_sig.decl.inputs[0].span.lo(), - arg.span.hi(), - arg.span.ctxt(), - ) + arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo()) } } else { impl_m_span diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 23ce4275d40..9cbd3f7bb33 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -313,6 +313,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => NoExpectation, }); let referent_ty = self.check_expr_with_expectation(expr, expected_inner); + self.require_type_is_sized(referent_ty, expr.span, traits::SizedBoxType); self.tcx.mk_box(referent_ty) } diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs new file mode 100644 index 00000000000..69a8970ae09 --- /dev/null +++ b/compiler/rustc_typeck/src/check/fallback.rs @@ -0,0 +1,157 @@ +use crate::check::FnCtxt; +use rustc_infer::infer::type_variable::Diverging; +use rustc_middle::ty::{self, Ty}; + +impl<'tcx> FnCtxt<'_, 'tcx> { + pub(super) fn type_inference_fallback(&self) { + // All type checking constraints were added, try to fallback unsolved variables. + self.select_obligations_where_possible(false, |_| {}); + let mut fallback_has_occurred = false; + + // We do fallback in two passes, to try to generate + // better error messages. + // The first time, we do *not* replace opaque types. + for ty in &self.unsolved_variables() { + debug!("unsolved_variable = {:?}", ty); + fallback_has_occurred |= self.fallback_if_possible(ty); + } + + // We now see if we can make progress. This might + // cause us to unify inference variables for opaque types, + // since we may have unified some other type variables + // during the first phase of fallback. + // This means that we only replace inference variables with their underlying + // opaque types as a last resort. + // + // In code like this: + // + // ```rust + // type MyType = impl Copy; + // fn produce() -> MyType { true } + // fn bad_produce() -> MyType { panic!() } + // ``` + // + // we want to unify the opaque inference variable in `bad_produce` + // with the diverging fallback for `panic!` (e.g. `()` or `!`). + // This will produce a nice error message about conflicting concrete + // types for `MyType`. + // + // If we had tried to fallback the opaque inference variable to `MyType`, + // we will generate a confusing type-check error that does not explicitly + // refer to opaque types. + self.select_obligations_where_possible(fallback_has_occurred, |_| {}); + + // We now run fallback again, but this time we allow it to replace + // unconstrained opaque type variables, in addition to performing + // other kinds of fallback. + for ty in &self.unsolved_variables() { + fallback_has_occurred |= self.fallback_opaque_type_vars(ty); + } + + // See if we can make any more progress. + self.select_obligations_where_possible(fallback_has_occurred, |_| {}); + } + + // Tries to apply a fallback to `ty` if it is an unsolved variable. + // + // - Unconstrained ints are replaced with `i32`. + // + // - Unconstrained floats are replaced with with `f64`. + // + // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]` + // is enabled. Otherwise, they are replaced with `()`. + // + // Fallback becomes very dubious if we have encountered type-checking errors. + // In that case, fallback to Error. + // The return value indicates whether fallback has occurred. + fn fallback_if_possible(&self, ty: Ty<'tcx>) -> bool { + // Careful: we do NOT shallow-resolve `ty`. We know that `ty` + // is an unsolved variable, and we determine its fallback based + // solely on how it was created, not what other type variables + // it may have been unified with since then. + // + // The reason this matters is that other attempts at fallback may + // (in principle) conflict with this fallback, and we wish to generate + // a type error in that case. (However, this actually isn't true right now, + // because we're only using the builtin fallback rules. This would be + // true if we were using user-supplied fallbacks. But it's still useful + // to write the code to detect bugs.) + // + // (Note though that if we have a general type variable `?T` that is then unified + // with an integer type variable `?I` that ultimately never gets + // resolved to a special integral type, `?T` is not considered unsolved, + // but `?I` is. The same is true for float variables.) + let fallback = match ty.kind() { + _ if self.is_tainted_by_errors() => self.tcx.ty_error(), + ty::Infer(ty::IntVar(_)) => self.tcx.types.i32, + ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64, + _ => match self.type_var_diverges(ty) { + Diverging::Diverges => self.tcx.mk_diverging_default(), + Diverging::NotDiverging => return false, + }, + }; + debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback); + + let span = self + .infcx + .type_var_origin(ty) + .map(|origin| origin.span) + .unwrap_or(rustc_span::DUMMY_SP); + self.demand_eqtype(span, ty, fallback); + true + } + + /// Second round of fallback: Unconstrained type variables + /// created from the instantiation of an opaque + /// type fall back to the opaque type itself. This is a + /// somewhat incomplete attempt to manage "identity passthrough" + /// for `impl Trait` types. + /// + /// For example, in this code: + /// + ///``` + /// type MyType = impl Copy; + /// fn defining_use() -> MyType { true } + /// fn other_use() -> MyType { defining_use() } + /// ``` + /// + /// `defining_use` will constrain the instantiated inference + /// variable to `bool`, while `other_use` will constrain + /// the instantiated inference variable to `MyType`. + /// + /// When we process opaque types during writeback, we + /// will handle cases like `other_use`, and not count + /// them as defining usages + /// + /// However, we also need to handle cases like this: + /// + /// ```rust + /// pub type Foo = impl Copy; + /// fn produce() -> Option<Foo> { + /// None + /// } + /// ``` + /// + /// In the above snippet, the inference variable created by + /// instantiating `Option<Foo>` will be completely unconstrained. + /// We treat this as a non-defining use by making the inference + /// variable fall back to the opaque type itself. + fn fallback_opaque_type_vars(&self, ty: Ty<'tcx>) -> bool { + let span = self + .infcx + .type_var_origin(ty) + .map(|origin| origin.span) + .unwrap_or(rustc_span::DUMMY_SP); + let oty = self.inner.borrow().opaque_types_vars.get(ty).map(|v| *v); + if let Some(opaque_ty) = oty { + debug!( + "fallback_opaque_type_vars(ty={:?}): falling back to opaque type {:?}", + ty, opaque_ty + ); + self.demand_eqtype(span, ty, opaque_ty); + true + } else { + return false; + } + } +} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index c13901ae8be..bb80f0879a4 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -4,7 +4,7 @@ use crate::astconv::{ }; use crate::check::callee::{self, DeferredCallResolution}; use crate::check::method::{self, MethodCallee, SelfSource}; -use crate::check::{BreakableCtxt, Diverges, Expectation, FallbackMode, FnCtxt, LocalTy}; +use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::captures::Captures; @@ -635,83 +635,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // Tries to apply a fallback to `ty` if it is an unsolved variable. - // - // - Unconstrained ints are replaced with `i32`. - // - // - Unconstrained floats are replaced with with `f64`. - // - // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]` - // is enabled. Otherwise, they are replaced with `()`. - // - // Fallback becomes very dubious if we have encountered type-checking errors. - // In that case, fallback to Error. - // The return value indicates whether fallback has occurred. - pub(in super::super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool { - use rustc_middle::ty::error::UnconstrainedNumeric::Neither; - use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; - - assert!(ty.is_ty_infer()); - let fallback = match self.type_is_unconstrained_numeric(ty) { - _ if self.is_tainted_by_errors() => self.tcx().ty_error(), - UnconstrainedInt => self.tcx.types.i32, - UnconstrainedFloat => self.tcx.types.f64, - Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(), - Neither => { - // This type variable was created from the instantiation of an opaque - // type. The fact that we're attempting to perform fallback for it - // means that the function neither constrained it to a concrete - // type, nor to the opaque type itself. - // - // For example, in this code: - // - //``` - // type MyType = impl Copy; - // fn defining_use() -> MyType { true } - // fn other_use() -> MyType { defining_use() } - // ``` - // - // `defining_use` will constrain the instantiated inference - // variable to `bool`, while `other_use` will constrain - // the instantiated inference variable to `MyType`. - // - // When we process opaque types during writeback, we - // will handle cases like `other_use`, and not count - // them as defining usages - // - // However, we also need to handle cases like this: - // - // ```rust - // pub type Foo = impl Copy; - // fn produce() -> Option<Foo> { - // None - // } - // ``` - // - // In the above snippet, the inference variable created by - // instantiating `Option<Foo>` will be completely unconstrained. - // We treat this as a non-defining use by making the inference - // variable fall back to the opaque type itself. - if let FallbackMode::All = mode { - if let Some(opaque_ty) = self.infcx.inner.borrow().opaque_types_vars.get(ty) { - debug!( - "fallback_if_possible: falling back opaque type var {:?} to {:?}", - ty, opaque_ty - ); - *opaque_ty - } else { - return false; - } - } else { - return false; - } - } - }; - debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback); - self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback); - true - } - pub(in super::super) fn select_all_obligations_or_error(&self) { debug!("select_all_obligations_or_error"); if let Err(errors) = self @@ -807,6 +730,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation)) } ty::PredicateKind::Subtype(..) => None, + ty::PredicateKind::Coerce(..) => None, ty::PredicateKind::RegionOutlives(..) => None, ty::PredicateKind::TypeOutlives(..) => None, ty::PredicateKind::WellFormed(..) => None, diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs index 9e3292d7821..b5bc9d3599a 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -265,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Get the number of generics the self type has (if an Adt) unless we can determine that // the user has written the self type with generics already which we (naively) do by looking // for a "<" in `self_ty_name`. - Adt(def, _) if !self_ty_name.contains("<") => self.tcx.generics_of(def.did).count(), + Adt(def, _) if !self_ty_name.contains('<') => self.tcx.generics_of(def.did).count(), _ => 0, }; let self_ty_generics = if self_ty_generics_count > 0 { diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index c6e6c8c5d70..486e4d15d65 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -841,6 +841,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Projection(..) | ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::WellFormed(..) diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index d1e583ed184..ad7e96e2833 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -75,6 +75,7 @@ mod diverges; pub mod dropck; mod expectation; mod expr; +mod fallback; mod fn_ctxt; mod gather_locals; mod generator_interior; @@ -445,50 +446,7 @@ fn typeck_with_fallback<'tcx>( fcx }; - // All type checking constraints were added, try to fallback unsolved variables. - fcx.select_obligations_where_possible(false, |_| {}); - let mut fallback_has_occurred = false; - - // We do fallback in two passes, to try to generate - // better error messages. - // The first time, we do *not* replace opaque types. - for ty in &fcx.unsolved_variables() { - fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::NoOpaque); - } - // We now see if we can make progress. This might - // cause us to unify inference variables for opaque types, - // since we may have unified some other type variables - // during the first phase of fallback. - // This means that we only replace inference variables with their underlying - // opaque types as a last resort. - // - // In code like this: - // - // ```rust - // type MyType = impl Copy; - // fn produce() -> MyType { true } - // fn bad_produce() -> MyType { panic!() } - // ``` - // - // we want to unify the opaque inference variable in `bad_produce` - // with the diverging fallback for `panic!` (e.g. `()` or `!`). - // This will produce a nice error message about conflicting concrete - // types for `MyType`. - // - // If we had tried to fallback the opaque inference variable to `MyType`, - // we will generate a confusing type-check error that does not explicitly - // refer to opaque types. - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - // We now run fallback again, but this time we allow it to replace - // unconstrained opaque type variables, in addition to performing - // other kinds of fallback. - for ty in &fcx.unsolved_variables() { - fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::All); - } - - // See if we can make any more progress. - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); + fcx.type_inference_fallback(); // Even though coercion casts provide type hints, we check casts after fallback for // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. @@ -914,16 +872,6 @@ enum TupleArgumentsFlag { TupleArguments, } -/// Controls how we perform fallback for unconstrained -/// type variables. -enum FallbackMode { - /// Do not fallback type variables to opaque types. - NoOpaque, - /// Perform all possible kinds of fallback, including - /// turning type variables to opaque types. - All, -} - /// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field. #[derive(Copy, Clone)] struct MaybeInProgressTables<'a, 'tcx> { diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs index f039790eca1..1c36335be81 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs @@ -222,8 +222,8 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> { let id_to_set = *ids.iter().min().unwrap(); // Sort the id list so that the algorithm is deterministic - let mut ids = ids.into_iter().collect::<SmallVec<[_; 8]>>(); - ids.sort(); + let mut ids = ids.into_iter().collect::<SmallVec<[usize; 8]>>(); + ids.sort_unstable(); let mut region = connected_regions.remove(&id_to_set).unwrap(); region.idents.extend_from_slice(&idents_to_add); @@ -266,8 +266,8 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> { // for each pair of impl blocks in the same connected region. for (_id, region) in connected_regions.into_iter() { let mut impl_blocks = - region.impl_blocks.into_iter().collect::<SmallVec<[_; 8]>>(); - impl_blocks.sort(); + region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>(); + impl_blocks.sort_unstable(); for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() { let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx]; for &impl2_items_idx in impl_blocks[(i + 1)..].iter() { diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 3b241317aa3..b0c95939bb7 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -269,6 +269,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { if def.variants.len() > 1 { needs_to_be_read = true; } + } else { + // If it is not ty::Adt, then it should be read + needs_to_be_read = true; } } PatKind::Lit(_) | PatKind::Range(..) => { diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index d5d81603fc5..e148370a036 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -407,6 +407,7 @@ fn trait_predicate_kind<'tcx>( | ty::PredicateKind::Projection(_) | ty::PredicateKind::WellFormed(_) | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::Coerce(_) | ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::ConstEvaluatable(..) diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs index 6e5be87928d..2ac1a18cffa 100644 --- a/compiler/rustc_typeck/src/outlives/explicit.rs +++ b/compiler/rustc_typeck/src/outlives/explicit.rs @@ -56,6 +56,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => (), diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 9a2205420a1..e4b28204158 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2129,16 +2129,32 @@ impl<T, A: Allocator> VecDeque<T, A> { F: FnMut(&T) -> bool, { let len = self.len(); - let mut del = 0; - for i in 0..len { - if !f(&self[i]) { - del += 1; - } else if del > 0 { - self.swap(i - del, i); + let mut idx = 0; + let mut cur = 0; + + // Stage 1: All values are retained. + while cur < len { + if !f(&self[cur]) { + cur += 1; + break; } + cur += 1; + idx += 1; } - if del > 0 { - self.truncate(len - del); + // Stage 2: Swap retained value into current idx. + while cur < len { + if !f(&self[cur]) { + cur += 1; + continue; + } + + self.swap(idx, cur); + cur += 1; + idx += 1; + } + // Stage 3: Trancate all values after idx. + if cur != idx { + self.truncate(idx); } } diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index 6116cfe1d01..56257e43462 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -42,6 +42,39 @@ fn bench_pop_back_100(b: &mut test::Bencher) { #[bench] #[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks +fn bench_retain_whole_10000(b: &mut test::Bencher) { + let v = (1..100000).collect::<VecDeque<u32>>(); + + b.iter(|| { + let mut v = v.clone(); + v.retain(|x| *x > 0) + }) +} + +#[bench] +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks +fn bench_retain_odd_10000(b: &mut test::Bencher) { + let v = (1..100000).collect::<VecDeque<u32>>(); + + b.iter(|| { + let mut v = v.clone(); + v.retain(|x| x & 1 == 0) + }) +} + +#[bench] +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks +fn bench_retain_half_10000(b: &mut test::Bencher) { + let v = (1..100000).collect::<VecDeque<u32>>(); + + b.iter(|| { + let mut v = v.clone(); + v.retain(|x| *x > 50000) + }) +} + +#[bench] +#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks fn bench_pop_front_100(b: &mut test::Bencher) { let mut deq = VecDeque::<i32>::with_capacity(101); diff --git a/library/backtrace b/library/backtrace -Subproject 221483ebaf45df5c956adffee2d4024e7c3b96b +Subproject 4f925f8d81dfa57067537217e501e1dff743349 diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index aa91346851f..56fad602cf9 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -374,16 +374,18 @@ where /// # Examples /// ``` /// let mut tuple = (vec![0], vec![1]); - /// tuple.extend(vec![(2, 3), (4, 5), (6, 7)]); - /// assert_eq!(tuple.0, vec![0, 2, 4, 6]); - /// assert_eq!(tuple.1, vec![1, 3, 5, 7]); + /// tuple.extend([(2, 3), (4, 5), (6, 7)]); + /// assert_eq!(tuple.0, [0, 2, 4, 6]); + /// assert_eq!(tuple.1, [1, 3, 5, 7]); /// - /// // also allows for arbitrarily nested tuples - /// let mut nested_tuple = (vec![(1, -1)], vec![(2, -2)]); - /// nested_tuple.extend(vec![((3, -3), (4, -4)), ((5, -5), (6, -6))]); + /// // also allows for arbitrarily nested tuples as elements + /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); + /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); /// - /// assert_eq!(nested_tuple.0, vec![(1, -1), (3, -3), (5, -5)]); - /// assert_eq!(nested_tuple.1, vec![(2, -2), (4, -4), (6, -6)]); + /// let (a, (b, c)) = nested_tuple; + /// assert_eq!(a, [1, 4, 7]); + /// assert_eq!(b, [2, 5, 8]); + /// assert_eq!(c, [3, 6, 9]); /// ``` fn extend<T: IntoIterator<Item = (A, B)>>(&mut self, into_iter: T) { let (a, b) = self; diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 7e260aaa428..38291b3f57e 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -23,11 +23,11 @@ hashbrown = { version = "0.11", default-features = false, features = ['rustc-dep std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate -addr2line = { version = "0.14.0", optional = true, default-features = false } +addr2line = { version = "0.16.0", optional = true, default-features = false } rustc-demangle = { version = "0.1.18", features = ['rustc-dep-of-std'] } miniz_oxide = { version = "0.4.0", optional = true, default-features = false } [dependencies.object] -version = "0.22" +version = "0.26.1" optional = true default-features = false features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 2c04481c04e..bdb172907ff 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -587,6 +587,12 @@ impl File { } } +// In addition to the `impl`s here, `File` also has `impl`s for +// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From<OwnedHandle>`/`Into<OwnedHandle>` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + impl AsInner<fs_imp::File> for File { fn as_inner(&self) -> &fs_imp::File { &self.inner diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 325acf0b979..336891ec1eb 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -546,6 +546,12 @@ impl TcpStream { } } +// In addition to the `impl`s here, `TcpStream` also has `impl`s for +// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsSocket`/`From<OwnedSocket>`/`Into<OwnedSocket>` and +// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows. + #[stable(feature = "rust1", since = "1.0.0")] impl Read for TcpStream { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { @@ -767,17 +773,24 @@ impl TcpListener { /// # Examples /// /// ```no_run - /// use std::net::TcpListener; + /// use std::net::{TcpListener, TcpStream}; /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// fn handle_connection(stream: TcpStream) { + /// //... + /// } /// - /// for stream in listener.incoming() { - /// match stream { - /// Ok(stream) => { - /// println!("new client!"); + /// fn main() -> std::io::Result<()> { + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// handle_connection(stream); + /// } + /// Err(e) => { /* connection failed */ } /// } - /// Err(e) => { /* connection failed */ } /// } + /// Ok(()) /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -908,6 +921,12 @@ impl TcpListener { } } +// In addition to the `impl`s here, `TcpListener` also has `impl`s for +// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsSocket`/`From<OwnedSocket>`/`Into<OwnedSocket>` and +// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows. + #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Iterator for Incoming<'a> { type Item = io::Result<TcpStream>; diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index d2088a12b2c..871505843af 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -779,6 +779,12 @@ impl UdpSocket { } } +// In addition to the `impl`s here, `UdpSocket` also has `impl`s for +// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsSocket`/`From<OwnedSocket>`/`Into<OwnedSocket>` and +// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows. + impl AsInner<net_imp::UdpSocket> for UdpSocket { fn as_inner(&self) -> &net_imp::UdpSocket { &self.0 diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index fbed3d32d45..a51113dd9e7 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -2,7 +2,6 @@ use crate::io::ErrorKind; use crate::net::test::{next_test_ip4, next_test_ip6}; use crate::net::*; use crate::sync::mpsc::channel; -use crate::sys_common::AsInner; use crate::thread; use crate::time::{Duration, Instant}; @@ -173,7 +172,7 @@ fn debug() { let socket_addr = next_test_ip4(); let udpsock = t!(UdpSocket::bind(&socket_addr)); - let udpsock_inner = udpsock.0.socket().as_inner(); + let udpsock_inner = udpsock.0.socket().as_raw(); let compare = format!("UdpSocket {{ addr: {:?}, {}: {:?} }}", socket_addr, name, udpsock_inner); assert_eq!(format!("{:?}", udpsock), compare); } diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs new file mode 100644 index 00000000000..df11dc21aa7 --- /dev/null +++ b/library/std/src/os/fd/mod.rs @@ -0,0 +1,13 @@ +//! Owned and borrowed Unix-like file descriptors. + +#![unstable(feature = "io_safety", issue = "87074")] +#![deny(unsafe_op_in_unsafe_fn)] + +// `RawFd`, `AsRawFd`, etc. +pub mod raw; + +// `OwnedFd`, `AsFd`, etc. +pub mod owned; + +// Implementations for `AsRawFd` etc. for network types. +mod net; diff --git a/library/std/src/os/unix/net/raw_fd.rs b/library/std/src/os/fd/net.rs index b3f12844101..843f45f7f5f 100644 --- a/library/std/src/os/unix/net/raw_fd.rs +++ b/library/std/src/os/fd/net.rs @@ -1,4 +1,5 @@ -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::fd::owned::OwnedFd; +use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; use crate::{net, sys}; @@ -8,7 +9,7 @@ macro_rules! impl_as_raw_fd { impl AsRawFd for net::$t { #[inline] fn as_raw_fd(&self) -> RawFd { - *self.as_inner().socket().as_inner() + self.as_inner().socket().as_raw_fd() } } )*}; @@ -21,8 +22,10 @@ macro_rules! impl_from_raw_fd { impl FromRawFd for net::$t { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> net::$t { - let socket = sys::net::Socket::from_inner(fd); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + unsafe { + let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); + net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + } } } )*}; @@ -35,7 +38,7 @@ macro_rules! impl_into_raw_fd { impl IntoRawFd for net::$t { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_socket().into_inner() + self.into_inner().into_socket().into_inner().into_inner().into_raw_fd() } } )*}; diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs new file mode 100644 index 00000000000..52d7d4690d3 --- /dev/null +++ b/library/std/src/os/fd/owned.rs @@ -0,0 +1,289 @@ +//! Owned and borrowed Unix-like file descriptors. + +#![unstable(feature = "io_safety", issue = "87074")] +#![deny(unsafe_op_in_unsafe_fn)] + +use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::fmt; +use crate::fs; +use crate::marker::PhantomData; +use crate::mem::forget; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// A borrowed file descriptor. +/// +/// This has a lifetime parameter to tie it to the lifetime of something that +/// owns the file descriptor. +/// +/// This uses `repr(transparent)` and has the representation of a host file +/// descriptor, so it can be used in FFI in places where a file descriptor is +/// passed as an argument, it is not captured or consumed, and it never has the +/// value `-1`. +#[derive(Copy, Clone)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a +// 32-bit c_int. Below is -2, in two's complement, but that only works out +// because c_int is 32 bits. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct BorrowedFd<'fd> { + fd: RawFd, + _phantom: PhantomData<&'fd OwnedFd>, +} + +/// An owned file descriptor. +/// +/// This closes the file descriptor on drop. +/// +/// This uses `repr(transparent)` and has the representation of a host file +/// descriptor, so it can be used in FFI in places where a file descriptor is +/// passed as a consumed argument or returned as an owned value, and it never +/// has the value `-1`. +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a +// 32-bit c_int. Below is -2, in two's complement, but that only works out +// because c_int is 32 bits. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct OwnedFd { + fd: RawFd, +} + +impl BorrowedFd<'_> { + /// Return a `BorrowedFd` holding the given raw file descriptor. + /// + /// # Safety + /// + /// The resource pointed to by `fd` must remain open for the duration of + /// the returned `BorrowedFd`, and it must not have the value `-1`. + #[inline] + #[unstable(feature = "io_safety", issue = "87074")] + pub unsafe fn borrow_raw_fd(fd: RawFd) -> Self { + assert_ne!(fd, u32::MAX as RawFd); + // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) + unsafe { Self { fd, _phantom: PhantomData } } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsRawFd for BorrowedFd<'_> { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsRawFd for OwnedFd { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl IntoRawFd for OwnedFd { + #[inline] + fn into_raw_fd(self) -> RawFd { + let fd = self.fd; + forget(self); + fd + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl FromRawFd for OwnedFd { + /// Constructs a new instance of `Self` from the given raw file descriptor. + /// + /// # Safety + /// + /// The resource pointed to by `fd` must be open and suitable for assuming + /// ownership. The resource must not require any cleanup other than `close`. + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> Self { + assert_ne!(fd, u32::MAX as RawFd); + // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) + unsafe { Self { fd } } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl Drop for OwnedFd { + #[inline] + fn drop(&mut self) { + unsafe { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // opened after we closed ours. + let _ = libc::close(self.fd); + } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl fmt::Debug for BorrowedFd<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BorrowedFd").field("fd", &self.fd).finish() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl fmt::Debug for OwnedFd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedFd").field("fd", &self.fd).finish() + } +} + +/// A trait to borrow the file descriptor from an underlying object. +/// +/// This is only available on unix platforms and must be imported in order to +/// call the method. Windows platforms have a corresponding `AsHandle` and +/// `AsSocket` set of traits. +#[unstable(feature = "io_safety", issue = "87074")] +pub trait AsFd { + /// Borrows the file descriptor. + /// + /// # Example + /// + /// ```rust,no_run + /// # #![feature(io_safety)] + /// use std::fs::File; + /// # use std::io; + /// # #[cfg(target_os = "wasi")] + /// # use std::os::wasi::io::{AsFd, BorrowedFd}; + /// # #[cfg(unix)] + /// # use std::os::unix::io::{AsFd, BorrowedFd}; + /// + /// let mut f = File::open("foo.txt")?; + /// # #[cfg(any(unix, target_os = "wasi"))] + /// let borrowed_fd: BorrowedFd<'_> = f.as_fd(); + /// # Ok::<(), io::Error>(()) + /// ``` + #[unstable(feature = "io_safety", issue = "87074")] + fn as_fd(&self) -> BorrowedFd<'_>; +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for BorrowedFd<'_> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + *self + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for OwnedFd { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // Safety: `OwnedFd` and `BorrowedFd` have the same validity + // invariants, and the `BorrowdFd` is bounded by the lifetime + // of `&self`. + unsafe { BorrowedFd::borrow_raw_fd(self.as_raw_fd()) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for fs::File { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<fs::File> for OwnedFd { + #[inline] + fn from(file: fs::File) -> OwnedFd { + file.into_inner().into_inner().into_inner() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<OwnedFd> for fs::File { + #[inline] + fn from(owned_fd: OwnedFd) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd))) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::net::TcpStream { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().socket().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<crate::net::TcpStream> for OwnedFd { + #[inline] + fn from(tcp_stream: crate::net::TcpStream) -> OwnedFd { + tcp_stream.into_inner().into_socket().into_inner().into_inner().into() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<OwnedFd> for crate::net::TcpStream { + #[inline] + fn from(owned_fd: OwnedFd) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner( + owned_fd, + )))) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::net::TcpListener { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().socket().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<crate::net::TcpListener> for OwnedFd { + #[inline] + fn from(tcp_listener: crate::net::TcpListener) -> OwnedFd { + tcp_listener.into_inner().into_socket().into_inner().into_inner().into() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<OwnedFd> for crate::net::TcpListener { + #[inline] + fn from(owned_fd: OwnedFd) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner( + owned_fd, + )))) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::net::UdpSocket { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().socket().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<crate::net::UdpSocket> for OwnedFd { + #[inline] + fn from(udp_socket: crate::net::UdpSocket) -> OwnedFd { + udp_socket.into_inner().into_socket().into_inner().into_inner().into() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<OwnedFd> for crate::net::UdpSocket { + #[inline] + fn from(owned_fd: OwnedFd) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner( + owned_fd, + )))) + } +} diff --git a/library/std/src/os/unix/io.rs b/library/std/src/os/fd/raw.rs index 07c30bfa9ed..f874cf0b42d 100644 --- a/library/std/src/os/unix/io.rs +++ b/library/std/src/os/fd/raw.rs @@ -1,23 +1,25 @@ -//! Unix-specific extensions to general I/O primitives. +//! Raw Unix-like file descriptors. #![stable(feature = "rust1", since = "1.0.0")] use crate::fs; use crate::io; use crate::os::raw; -use crate::sys; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +#[cfg(unix)] +use crate::os::unix::io::OwnedFd; +#[cfg(target_os = "wasi")] +use crate::os::wasi::io::OwnedFd; +use crate::sys_common::{AsInner, IntoInner}; /// Raw file descriptors. #[stable(feature = "rust1", since = "1.0.0")] pub type RawFd = raw::c_int; -/// A trait to extract the raw unix file descriptor from an underlying -/// object. +/// A trait to extract the raw file descriptor from an underlying object. /// -/// This is only available on unix platforms and must be imported in order -/// to call the method. Windows platforms have a corresponding `AsRawHandle` -/// and `AsRawSocket` set of traits. +/// This is only available on unix and WASI platforms and must be imported in +/// order to call the method. Windows platforms have a corresponding +/// `AsRawHandle` and `AsRawSocket` set of traits. #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRawFd { /// Extracts the raw file descriptor. @@ -31,10 +33,14 @@ pub trait AsRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; + /// #[cfg(unix)] /// use std::os::unix::io::{AsRawFd, RawFd}; + /// #[cfg(target_os = "wasi")] + /// use std::os::wasi::io::{AsRawFd, RawFd}; /// /// let mut f = File::open("foo.txt")?; /// // Note that `raw_fd` is only valid as long as `f` exists. + /// #[cfg(any(unix, target_os = "wasi"))] /// let raw_fd: RawFd = f.as_raw_fd(); /// # Ok::<(), io::Error>(()) /// ``` @@ -64,12 +70,17 @@ pub trait FromRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; + /// #[cfg(unix)] /// use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd}; + /// #[cfg(target_os = "wasi")] + /// use std::os::wasi::io::{FromRawFd, IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; + /// # #[cfg(any(unix, target_os = "wasi"))] /// let raw_fd: RawFd = f.into_raw_fd(); /// // SAFETY: no other functions should call `from_raw_fd`, so there /// // is only one owner for the file descriptor. + /// # #[cfg(any(unix, target_os = "wasi"))] /// let f = unsafe { File::from_raw_fd(raw_fd) }; /// # Ok::<(), io::Error>(()) /// ``` @@ -92,9 +103,13 @@ pub trait IntoRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; + /// #[cfg(unix)] /// use std::os::unix::io::{IntoRawFd, RawFd}; + /// #[cfg(target_os = "wasi")] + /// use std::os::wasi::io::{IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; + /// #[cfg(any(unix, target_os = "wasi"))] /// let raw_fd: RawFd = f.into_raw_fd(); /// # Ok::<(), io::Error>(()) /// ``` @@ -128,21 +143,21 @@ impl FromRawFd for RawFd { impl AsRawFd for fs::File { #[inline] fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() + self.as_inner().as_raw_fd() } } #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawFd for fs::File { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs::File::from_inner(fd)) + unsafe { fs::File::from(OwnedFd::from_raw_fd(fd)) } } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawFd for fs::File { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() + self.into_inner().into_inner().into_raw_fd() } } diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index 6daff0f003c..e3e7143c851 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -3,7 +3,7 @@ #![unstable(feature = "linux_pidfd", issue = "82971")] use crate::io::Result; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::process; use crate::sealed::Sealed; #[cfg(not(doc))] @@ -69,19 +69,37 @@ impl IntoInner<FileDesc> for PidFd { impl AsRawFd for PidFd { fn as_raw_fd(&self) -> RawFd { - self.as_inner().raw() + self.as_inner().as_raw_fd() } } impl FromRawFd for PidFd { unsafe fn from_raw_fd(fd: RawFd) -> Self { - Self::from_inner(FileDesc::new(fd)) + Self::from_inner(FileDesc::from_raw_fd(fd)) } } impl IntoRawFd for PidFd { fn into_raw_fd(self) -> RawFd { - self.into_inner().into_raw() + self.into_inner().into_raw_fd() + } +} + +impl AsFd for PidFd { + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +impl From<OwnedFd> for PidFd { + fn from(fd: OwnedFd) -> Self { + Self::from_inner(FileDesc::from_inner(fd)) + } +} + +impl From<PidFd> for OwnedFd { + fn from(pid_fd: PidFd) -> Self { + pid_fd.into_inner().into_inner() } } diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 4c9814919cd..79e69673007 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -121,3 +121,6 @@ mod imp { #[cfg(not(doc))] #[stable(feature = "os", since = "1.0.0")] pub use imp::*; + +#[cfg(any(unix, target_os = "wasi", doc))] +mod fd; diff --git a/library/std/src/os/unix/io/fd.rs b/library/std/src/os/unix/io/fd.rs new file mode 100644 index 00000000000..7795db7abc0 --- /dev/null +++ b/library/std/src/os/unix/io/fd.rs @@ -0,0 +1,9 @@ +//! Owned and borrowed file descriptors. + +#![unstable(feature = "io_safety", issue = "87074")] + +// Tests for this module +#[cfg(test)] +mod tests; + +pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/unix/io/fd/tests.rs b/library/std/src/os/unix/io/fd/tests.rs new file mode 100644 index 00000000000..84d2a7a1a91 --- /dev/null +++ b/library/std/src/os/unix/io/fd/tests.rs @@ -0,0 +1,11 @@ +use crate::mem::size_of; +use crate::os::unix::io::RawFd; + +#[test] +fn test_raw_fd_layout() { + // `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start` + // and `rustc_layout_scalar_valid_range_end`, with values that depend on + // the bit width of `RawFd`. If this ever changes, those values will need + // to be updated. + assert_eq!(size_of::<RawFd>(), 4); +} diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs new file mode 100644 index 00000000000..0fd9591b016 --- /dev/null +++ b/library/std/src/os/unix/io/mod.rs @@ -0,0 +1,57 @@ +//! Unix-specific extensions to general I/O primitives. +//! +//! Just like raw pointers, raw file descriptors point to resources with +//! dynamic lifetimes, and they can dangle if they outlive their resources +//! or be forged if they're created from invalid values. +//! +//! This module provides three types for representing file descriptors, +//! with different ownership properties: raw, borrowed, and owned, which are +//! analogous to types used for representing pointers: +//! +//! | Type | Analogous to | +//! | ------------------ | ------------ | +//! | [`RawFd`] | `*const _` | +//! | [`BorrowedFd<'a>`] | `&'a _` | +//! | [`OwnedFd`] | `Box<_>` | +//! +//! Like raw pointers, `RawFd` values are primitive values. And in new code, +//! they should be considered unsafe to do I/O on (analogous to dereferencing +//! them). Rust did not always provide this guidance, so existing code in the +//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. Once the +//! `io_safety` feature is stable, libraries will be encouraged to migrate, +//! either by adding `unsafe` to APIs that dereference `RawFd` values, or by +//! using to `BorrowedFd` or `OwnedFd` instead. +//! +//! Like references, `BorrowedFd` values are tied to a lifetime, to ensure +//! that they don't outlive the resource they point to. These are safe to +//! use. `BorrowedFd` values may be used in APIs which provide safe access to +//! any system call except for: +//! - `close`, because that would end the dynamic lifetime of the resource +//! without ending the lifetime of the file descriptor. +//! - `dup2`/`dup3`, in the second argument, because this argument is +//! closed and assigned a new resource, which may break the assumptions +//! other code using that file descriptor. +//! This list doesn't include `mmap`, since `mmap` does do a proper borrow of +//! its file descriptor argument. That said, `mmap` is unsafe for other +//! reasons: it operates on raw pointers, and it can have undefined behavior if +//! the underlying storage is mutated. Mutations may come from other processes, +//! or from the same process if the API provides `BorrowedFd` access, since as +//! mentioned earlier, `BorrowedFd` values may be used in APIs which provide +//! safe access to any system call. Consequently, code using `mmap` and +//! presenting a safe API must take full responsibility for ensuring that safe +//! Rust code cannot evoke undefined behavior through it. +//! +//! Like boxes, `OwnedFd` values conceptually own the resource they point to, +//! and free (close) it when they are dropped. +//! +//! [`BorrowedFd<'a>`]: crate::os::unix::io::BorrowedFd + +#![stable(feature = "rust1", since = "1.0.0")] + +mod fd; +mod raw; + +#[unstable(feature = "io_safety", issue = "87074")] +pub use fd::*; +#[stable(feature = "rust1", since = "1.0.0")] +pub use raw::*; diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs new file mode 100644 index 00000000000..6317e317471 --- /dev/null +++ b/library/std/src/os/unix/io/raw.rs @@ -0,0 +1,5 @@ +//! Unix-specific extensions to general I/O primitives. + +#![stable(feature = "rust1", since = "1.0.0")] + +pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 6c73d4b21dd..17a02595724 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -108,7 +108,7 @@ pub mod prelude { pub use super::fs::{FileTypeExt, MetadataExt, OpenOptionsExt, PermissionsExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::process::{CommandExt, ExitStatusExt}; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 9e39f70f68e..f11eec18cc5 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -21,7 +21,7 @@ use super::{sockaddr_un, SocketAddr}; ))] use crate::io::{IoSlice, IoSliceMut}; use crate::net::Shutdown; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sys::cvt; use crate::sys::net::Socket; @@ -106,7 +106,7 @@ impl UnixDatagram { let socket = UnixDatagram::unbound()?; let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::bind(*socket.0.as_inner(), &addr as *const _ as *const _, len as _))?; + cvt(libc::bind(socket.as_raw_fd(), &addr as *const _ as *const _, len as _))?; Ok(socket) } @@ -187,7 +187,7 @@ impl UnixDatagram { unsafe { let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::connect(*self.0.as_inner(), &addr as *const _ as *const _, len))?; + cvt(libc::connect(self.as_raw_fd(), &addr as *const _ as *const _, len))?; } Ok(()) } @@ -229,7 +229,7 @@ impl UnixDatagram { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) }) } /// Returns the address of this socket's peer. @@ -253,7 +253,7 @@ impl UnixDatagram { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result<SocketAddr> { - SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getpeername(self.as_raw_fd(), addr, len) }) } fn recv_from_flags( @@ -264,7 +264,7 @@ impl UnixDatagram { let mut count = 0; let addr = SocketAddr::new(|addr, len| unsafe { count = libc::recvfrom( - *self.0.as_inner(), + self.as_raw_fd(), buf.as_mut_ptr() as *mut _, buf.len(), flags, @@ -462,7 +462,7 @@ impl UnixDatagram { let (addr, len) = sockaddr_un(path.as_ref())?; let count = cvt(libc::sendto( - *self.0.as_inner(), + self.as_raw_fd(), buf.as_ptr() as *const _, buf.len(), MSG_NOSIGNAL, @@ -881,7 +881,7 @@ impl UnixDatagram { impl AsRawFd for UnixDatagram { #[inline] fn as_raw_fd(&self) -> RawFd { - *self.0.as_inner() + self.0.as_inner().as_raw_fd() } } @@ -889,7 +889,7 @@ impl AsRawFd for UnixDatagram { impl FromRawFd for UnixDatagram { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { - UnixDatagram(Socket::from_inner(fd)) + UnixDatagram(Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)))) } } @@ -897,6 +897,30 @@ impl FromRawFd for UnixDatagram { impl IntoRawFd for UnixDatagram { #[inline] fn into_raw_fd(self) -> RawFd { - self.0.into_inner() + self.0.into_inner().into_inner().into_raw_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for UnixDatagram { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<UnixDatagram> for OwnedFd { + #[inline] + fn from(unix_datagram: UnixDatagram) -> OwnedFd { + unsafe { OwnedFd::from_raw_fd(unix_datagram.into_raw_fd()) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<OwnedFd> for UnixDatagram { + #[inline] + fn from(owned: OwnedFd) -> Self { + unsafe { Self::from_raw_fd(owned.into_raw_fd()) } } } diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index bdd08fe8380..9066c71794f 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -1,5 +1,5 @@ use super::{sockaddr_un, SocketAddr, UnixStream}; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sys::cvt; use crate::sys::net::Socket; @@ -74,8 +74,8 @@ impl UnixListener { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::bind(*inner.as_inner(), &addr as *const _ as *const _, len as _))?; - cvt(libc::listen(*inner.as_inner(), 128))?; + cvt(libc::bind(inner.as_inner().as_raw_fd(), &addr as *const _ as *const _, len as _))?; + cvt(libc::listen(inner.as_inner().as_raw_fd(), 128))?; Ok(UnixListener(inner)) } @@ -150,7 +150,7 @@ impl UnixListener { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) }) } /// Moves the socket into or out of nonblocking mode. @@ -242,7 +242,7 @@ impl UnixListener { impl AsRawFd for UnixListener { #[inline] fn as_raw_fd(&self) -> RawFd { - *self.0.as_inner() + self.0.as_inner().as_raw_fd() } } @@ -250,7 +250,7 @@ impl AsRawFd for UnixListener { impl FromRawFd for UnixListener { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> UnixListener { - UnixListener(Socket::from_inner(fd)) + UnixListener(Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)))) } } @@ -258,7 +258,7 @@ impl FromRawFd for UnixListener { impl IntoRawFd for UnixListener { #[inline] fn into_raw_fd(self) -> RawFd { - self.0.into_inner() + self.0.into_inner().into_inner().into_raw_fd() } } diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index 3088ffb5e5c..d462bd4b5f7 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -25,7 +25,6 @@ mod addr; mod ancillary; mod datagram; mod listener; -mod raw_fd; mod stream; #[cfg(all(test, not(target_os = "emscripten")))] mod tests; @@ -48,7 +47,5 @@ pub use self::ancillary::*; pub use self::datagram::*; #[stable(feature = "unix_socket", since = "1.10.0")] pub use self::listener::*; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::raw_fd::*; #[stable(feature = "unix_socket", since = "1.10.0")] pub use self::stream::*; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index fba084375e5..4119de3c03c 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -13,7 +13,7 @@ use super::{sockaddr_un, SocketAddr}; use crate::fmt; use crate::io::{self, Initializer, IoSlice, IoSliceMut}; use crate::net::Shutdown; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(any( target_os = "android", target_os = "linux", @@ -28,7 +28,7 @@ use crate::os::unix::ucred; use crate::path::Path; use crate::sys::cvt; use crate::sys::net::Socket; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner}; use crate::time::Duration; #[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")] @@ -101,7 +101,7 @@ impl UnixStream { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::connect(*inner.as_inner(), &addr as *const _ as *const _, len))?; + cvt(libc::connect(inner.as_raw_fd(), &addr as *const _ as *const _, len))?; Ok(UnixStream(inner)) } } @@ -167,7 +167,7 @@ impl UnixStream { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) }) } /// Returns the socket address of the remote half of this connection. @@ -185,7 +185,7 @@ impl UnixStream { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result<SocketAddr> { - SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getpeername(self.as_raw_fd(), addr, len) }) } /// Gets the peer credentials for this Unix domain socket. @@ -659,7 +659,7 @@ impl<'a> io::Write for &'a UnixStream { impl AsRawFd for UnixStream { #[inline] fn as_raw_fd(&self) -> RawFd { - *self.0.as_inner() + self.0.as_raw_fd() } } @@ -667,7 +667,7 @@ impl AsRawFd for UnixStream { impl FromRawFd for UnixStream { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> UnixStream { - UnixStream(Socket::from_inner(fd)) + UnixStream(Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)))) } } @@ -675,6 +675,30 @@ impl FromRawFd for UnixStream { impl IntoRawFd for UnixStream { #[inline] fn into_raw_fd(self) -> RawFd { - self.0.into_inner() + self.0.into_raw_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for UnixStream { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<UnixStream> for OwnedFd { + #[inline] + fn from(unix_stream: UnixStream) -> OwnedFd { + unsafe { OwnedFd::from_raw_fd(unix_stream.into_raw_fd()) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<OwnedFd> for UnixStream { + #[inline] + fn from(owned: OwnedFd) -> Self { + unsafe { Self::from_raw_fd(owned.into_raw_fd()) } } } diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 615290d2703..650dcbabbae 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -4,7 +4,7 @@ use crate::ffi::OsStr; use crate::io; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::process; use crate::sealed::Sealed; use crate::sys; @@ -321,7 +321,17 @@ impl ExitStatusExt for process::ExitStatusError { impl FromRawFd for process::Stdio { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { - let fd = sys::fd::FileDesc::new(fd); + let fd = sys::fd::FileDesc::from_raw_fd(fd); + let io = sys::process::Stdio::Fd(fd); + process::Stdio::from_inner(io) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<OwnedFd> for process::Stdio { + #[inline] + fn from(fd: OwnedFd) -> process::Stdio { + let fd = sys::fd::FileDesc::from_inner(fd); let io = sys::process::Stdio::Fd(fd); process::Stdio::from_inner(io) } @@ -331,7 +341,7 @@ impl FromRawFd for process::Stdio { impl AsRawFd for process::ChildStdin { #[inline] fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() + self.as_inner().as_raw_fd() } } @@ -339,7 +349,7 @@ impl AsRawFd for process::ChildStdin { impl AsRawFd for process::ChildStdout { #[inline] fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() + self.as_inner().as_raw_fd() } } @@ -347,7 +357,7 @@ impl AsRawFd for process::ChildStdout { impl AsRawFd for process::ChildStderr { #[inline] fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() + self.as_inner().as_raw_fd() } } @@ -355,7 +365,7 @@ impl AsRawFd for process::ChildStderr { impl IntoRawFd for process::ChildStdin { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() + self.into_inner().into_inner().into_raw_fd() } } @@ -363,7 +373,7 @@ impl IntoRawFd for process::ChildStdin { impl IntoRawFd for process::ChildStdout { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() + self.into_inner().into_inner().into_raw_fd() } } @@ -371,7 +381,55 @@ impl IntoRawFd for process::ChildStdout { impl IntoRawFd for process::ChildStderr { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() + self.into_inner().into_inner().into_raw_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::process::ChildStdin { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<crate::process::ChildStdin> for OwnedFd { + #[inline] + fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { + child_stdin.into_inner().into_inner().into_inner() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::process::ChildStdout { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<crate::process::ChildStdout> for OwnedFd { + #[inline] + fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { + child_stdout.into_inner().into_inner().into_inner() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::process::ChildStderr { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<crate::process::ChildStderr> for OwnedFd { + #[inline] + fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { + child_stderr.into_inner().into_inner().into_inner() } } diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index bd30d6ae3f3..3df27563e21 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -228,35 +228,35 @@ pub trait FileExt { impl FileExt for fs::File { fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { - self.as_inner().fd().pread(bufs, offset) + self.as_inner().as_inner().pread(bufs, offset) } fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { - self.as_inner().fd().pwrite(bufs, offset) + self.as_inner().as_inner().pwrite(bufs, offset) } fn tell(&self) -> io::Result<u64> { - self.as_inner().fd().tell() + self.as_inner().as_inner().tell() } fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> { - self.as_inner().fd().set_flags(flags) + self.as_inner().as_inner().set_flags(flags) } fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> { - self.as_inner().fd().set_rights(rights, inheriting) + self.as_inner().as_inner().set_rights(rights, inheriting) } fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { - self.as_inner().fd().advise(offset, len, advice) + self.as_inner().as_inner().advise(offset, len, advice) } fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - self.as_inner().fd().allocate(offset, len) + self.as_inner().as_inner().allocate(offset, len) } fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()> { - self.as_inner().fd().create_directory(osstr2str(dir.as_ref().as_ref())?) + self.as_inner().as_inner().create_directory(osstr2str(dir.as_ref().as_ref())?) } fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> { @@ -269,11 +269,11 @@ impl FileExt for fs::File { } fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { - self.as_inner().fd().unlink_file(osstr2str(path.as_ref().as_ref())?) + self.as_inner().as_inner().unlink_file(osstr2str(path.as_ref().as_ref())?) } fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { - self.as_inner().fd().remove_directory(osstr2str(path.as_ref().as_ref())?) + self.as_inner().as_inner().remove_directory(osstr2str(path.as_ref().as_ref())?) } } @@ -486,10 +486,10 @@ pub fn link<P: AsRef<Path>, U: AsRef<Path>>( new_fd: &File, new_path: U, ) -> io::Result<()> { - old_fd.as_inner().fd().link( + old_fd.as_inner().as_inner().link( old_flags, osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().fd(), + new_fd.as_inner().as_inner(), osstr2str(new_path.as_ref().as_ref())?, ) } @@ -503,9 +503,9 @@ pub fn rename<P: AsRef<Path>, U: AsRef<Path>>( new_fd: &File, new_path: U, ) -> io::Result<()> { - old_fd.as_inner().fd().rename( + old_fd.as_inner().as_inner().rename( osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().fd(), + new_fd.as_inner().as_inner(), osstr2str(new_path.as_ref().as_ref())?, ) } @@ -519,7 +519,7 @@ pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>( new_path: U, ) -> io::Result<()> { fd.as_inner() - .fd() + .as_inner() .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?) } diff --git a/library/std/src/os/wasi/io.rs b/library/std/src/os/wasi/io.rs deleted file mode 100644 index b6bc74da8e7..00000000000 --- a/library/std/src/os/wasi/io.rs +++ /dev/null @@ -1,208 +0,0 @@ -//! WASI-specific extensions to general I/O primitives - -#![deny(unsafe_op_in_unsafe_fn)] -#![unstable(feature = "wasi_ext", issue = "71213")] - -use crate::fs; -use crate::io; -use crate::net; -use crate::os::raw; -use crate::sys; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -/// Raw file descriptors. -/// -/// This has type `c_int` to ease compatibility with code that also compiles on -/// Unix configurations, however unlike Unix and POSIX, in WASI negative file -/// descriptors are valid. Only `-1` is reserved for indicating errors. Code -/// intending to be portable across Unix platforms and WASI should avoid -/// assuming that negative file descriptors are invalid. -pub type RawFd = raw::c_int; - -/// A trait to extract the raw WASI file descriptor from an underlying -/// object. -pub trait AsRawFd { - /// Extracts the raw file descriptor. - /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guaranteed to be valid while - /// the original object has not yet been destroyed. - fn as_raw_fd(&self) -> RawFd; -} - -/// A trait to express the ability to construct an object from a raw file -/// descriptor. -pub trait FromRawFd { - /// Constructs a new instance of `Self` from the given raw file - /// descriptor. - /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_fd(fd: RawFd) -> Self; -} - -/// A trait to express the ability to consume an object and acquire ownership of -/// its raw file descriptor. -pub trait IntoRawFd { - /// Consumes this object, returning the raw underlying file descriptor. - /// - /// This function **transfers ownership** of the underlying file descriptor - /// to the caller. Callers are then the unique owners of the file descriptor - /// and must close the descriptor once it's no longer needed. - fn into_raw_fd(self) -> RawFd; -} - -#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] -impl AsRawFd for RawFd { - #[inline] - fn as_raw_fd(&self) -> RawFd { - *self - } -} -#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] -impl IntoRawFd for RawFd { - #[inline] - fn into_raw_fd(self) -> RawFd { - self - } -} -#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] -impl FromRawFd for RawFd { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> RawFd { - fd - } -} - -impl AsRawFd for net::TcpStream { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for net::TcpStream { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { - net::TcpStream::from_inner(sys::net::TcpStream::from_inner(fd)) - } -} - -impl IntoRawFd for net::TcpStream { - #[inline] - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for net::TcpListener { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for net::TcpListener { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { - net::TcpListener::from_inner(sys::net::TcpListener::from_inner(fd)) - } -} - -impl IntoRawFd for net::TcpListener { - #[inline] - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for net::UdpSocket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for net::UdpSocket { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { - net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(fd)) - } -} - -impl IntoRawFd for net::UdpSocket { - #[inline] - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for fs::File { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for fs::File { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs::File::from_inner(fd)) - } -} - -impl IntoRawFd for fs::File { - #[inline] - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for io::Stdin { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDIN_FILENO - } -} - -impl AsRawFd for io::Stdout { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDOUT_FILENO - } -} - -impl AsRawFd for io::Stderr { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDERR_FILENO - } -} - -impl<'a> AsRawFd for io::StdinLock<'a> { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDIN_FILENO - } -} - -impl<'a> AsRawFd for io::StdoutLock<'a> { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDOUT_FILENO - } -} - -impl<'a> AsRawFd for io::StderrLock<'a> { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDERR_FILENO - } -} diff --git a/library/std/src/os/wasi/io/fd.rs b/library/std/src/os/wasi/io/fd.rs new file mode 100644 index 00000000000..930aca887e3 --- /dev/null +++ b/library/std/src/os/wasi/io/fd.rs @@ -0,0 +1,9 @@ +//! Owned and borrowed file descriptors. + +#![unstable(feature = "wasi_ext", issue = "71213")] + +// Tests for this module +#[cfg(test)] +mod tests; + +pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/wasi/io/fd/tests.rs b/library/std/src/os/wasi/io/fd/tests.rs new file mode 100644 index 00000000000..418274752b0 --- /dev/null +++ b/library/std/src/os/wasi/io/fd/tests.rs @@ -0,0 +1,11 @@ +use crate::mem::size_of; +use crate::os::wasi::io::RawFd; + +#[test] +fn test_raw_fd_layout() { + // `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start` + // and `rustc_layout_scalar_valid_range_end`, with values that depend on + // the bit width of `RawFd`. If this ever changes, those values will need + // to be updated. + assert_eq!(size_of::<RawFd>(), 4); +} diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs new file mode 100644 index 00000000000..6c884e2eaf4 --- /dev/null +++ b/library/std/src/os/wasi/io/mod.rs @@ -0,0 +1,12 @@ +//! WASI-specific extensions to general I/O primitives. + +#![deny(unsafe_op_in_unsafe_fn)] +#![unstable(feature = "wasi_ext", issue = "71213")] + +mod fd; +mod raw; + +#[unstable(feature = "wasi_ext", issue = "71213")] +pub use fd::*; +#[unstable(feature = "wasi_ext", issue = "71213")] +pub use raw::*; diff --git a/library/std/src/os/wasi/io/raw.rs b/library/std/src/os/wasi/io/raw.rs new file mode 100644 index 00000000000..0e0c5824e34 --- /dev/null +++ b/library/std/src/os/wasi/io/raw.rs @@ -0,0 +1,5 @@ +//! WASI-specific extensions to general I/O primitives. + +#![unstable(feature = "wasi_ext", issue = "71213")] + +pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/wasi/mod.rs b/library/std/src/os/wasi/mod.rs index 44b7c32e956..d767c149dc5 100644 --- a/library/std/src/os/wasi/mod.rs +++ b/library/std/src/os/wasi/mod.rs @@ -32,6 +32,7 @@ pub mod ffi; pub mod fs; pub mod io; +pub mod net; /// A prelude for conveniently writing platform-specific code. /// @@ -49,5 +50,5 @@ pub mod prelude { pub use super::fs::{DirEntryExt, FileExt, MetadataExt, OpenOptionsExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; } diff --git a/library/std/src/os/wasi/net/mod.rs b/library/std/src/os/wasi/net/mod.rs new file mode 100644 index 00000000000..e6bcf87887f --- /dev/null +++ b/library/std/src/os/wasi/net/mod.rs @@ -0,0 +1,3 @@ +//! WASI-specific networking functionality + +#![unstable(feature = "wasi_ext", issue = "71213")] diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs new file mode 100644 index 00000000000..72a17adb3b4 --- /dev/null +++ b/library/std/src/os/windows/io/handle.rs @@ -0,0 +1,386 @@ +//! Owned and borrowed OS handles. + +#![unstable(feature = "io_safety", issue = "87074")] + +use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; +use crate::convert::TryFrom; +use crate::ffi::c_void; +use crate::fmt; +use crate::fs; +use crate::marker::PhantomData; +use crate::mem::forget; +use crate::ptr::NonNull; +use crate::sys::c; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// A borrowed handle. +/// +/// This has a lifetime parameter to tie it to the lifetime of something that +/// owns the handle. +/// +/// This uses `repr(transparent)` and has the representation of a host handle, +/// so it can be used in FFI in places where a handle is passed as an argument, +/// it is not captured or consumed, and it is never null. +/// +/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is +/// sometimes a valid handle value. See [here] for the full story. +/// +/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 +#[derive(Copy, Clone)] +#[repr(transparent)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct BorrowedHandle<'handle> { + handle: NonNull<c_void>, + _phantom: PhantomData<&'handle OwnedHandle>, +} + +/// An owned handle. +/// +/// This closes the handle on drop. +/// +/// This uses `repr(transparent)` and has the representation of a host handle, +/// so it can be used in FFI in places where a handle is passed as a consumed +/// argument or returned as an owned value, and is never null. +/// +/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is +/// sometimes a valid handle value. See [here] for the full story. For APIs +/// like `CreateFileW` which report errors with `INVALID_HANDLE_VALUE` instead +/// of null, use [`HandleOrInvalid`] instead of `Option<OwnedHandle>`. +/// +/// `OwnedHandle` uses [`CloseHandle`] to close its handle on drop. As such, +/// it must not be used with handles to open registry keys which need to be +/// closed with [`RegCloseKey`] instead. +/// +/// [`CloseHandle`]: https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle +/// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey +/// +/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 +#[repr(transparent)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct OwnedHandle { + handle: NonNull<c_void>, +} + +/// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used +/// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses +/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such +/// FFI declarations. +/// +/// The only thing you can usefully do with a `HandleOrInvalid` is to convert it into an +/// `OwnedHandle` using its [`TryFrom`] implementation; this conversion takes care of the check for +/// `INVALID_HANDLE_VALUE`. This ensures that such FFI calls cannot start using the handle without +/// checking for `INVALID_HANDLE_VALUE` first. +/// +/// If this holds a valid handle, it will close the handle on drop. +#[repr(transparent)] +#[unstable(feature = "io_safety", issue = "87074")] +#[derive(Debug)] +pub struct HandleOrInvalid(Option<OwnedHandle>); + +// The Windows [`HANDLE`] type may be transferred across and shared between +// thread boundaries (despite containing a `*mut void`, which in general isn't +// `Send` or `Sync`). +// +// [`HANDLE`]: std::os::windows::raw::HANDLE +unsafe impl Send for OwnedHandle {} +unsafe impl Send for HandleOrInvalid {} +unsafe impl Send for BorrowedHandle<'_> {} +unsafe impl Sync for OwnedHandle {} +unsafe impl Sync for HandleOrInvalid {} +unsafe impl Sync for BorrowedHandle<'_> {} + +impl BorrowedHandle<'_> { + /// Return a `BorrowedHandle` holding the given raw handle. + /// + /// # Safety + /// + /// The resource pointed to by `handle` must be a valid open handle, it + /// must remain open for the duration of the returned `BorrowedHandle`, and + /// it must not be null. + /// + /// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is + /// sometimes a valid handle value. See [here] for the full story. + /// + /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 + #[inline] + #[unstable(feature = "io_safety", issue = "87074")] + pub unsafe fn borrow_raw_handle(handle: RawHandle) -> Self { + assert!(!handle.is_null()); + Self { handle: NonNull::new_unchecked(handle), _phantom: PhantomData } + } +} + +impl TryFrom<HandleOrInvalid> for OwnedHandle { + type Error = (); + + #[inline] + fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, ()> { + // In theory, we ought to be able to assume that the pointer here is + // never null, use `OwnedHandle` rather than `Option<OwnedHandle>`, and + // obviate the the panic path here. Unfortunately, Win32 documentation + // doesn't explicitly guarantee this anywhere. + // + // APIs like [`CreateFileW`] itself have `HANDLE` arguments where a + // null handle indicates an absent value, which wouldn't work if null + // were a valid handle value, so it seems very unlikely that it could + // ever return null. But who knows? + // + // [`CreateFileW`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew + let owned_handle = handle_or_invalid.0.expect("A `HandleOrInvalid` was null!"); + if owned_handle.handle.as_ptr() == c::INVALID_HANDLE_VALUE { + Err(()) + } else { + Ok(owned_handle) + } + } +} + +impl AsRawHandle for BorrowedHandle<'_> { + #[inline] + fn as_raw_handle(&self) -> RawHandle { + self.handle.as_ptr() + } +} + +impl AsRawHandle for OwnedHandle { + #[inline] + fn as_raw_handle(&self) -> RawHandle { + self.handle.as_ptr() + } +} + +impl IntoRawHandle for OwnedHandle { + #[inline] + fn into_raw_handle(self) -> RawHandle { + let handle = self.handle.as_ptr(); + forget(self); + handle + } +} + +impl FromRawHandle for OwnedHandle { + /// Constructs a new instance of `Self` from the given raw handle. + /// + /// Use `HandleOrInvalid` instead of `Option<OwnedHandle>` for APIs that + /// use `INVALID_HANDLE_VALUE` to indicate failure. + /// + /// # Safety + /// + /// The resource pointed to by `handle` must be open and suitable for + /// assuming ownership. The resource must not require any cleanup other + /// than `CloseHandle`. + /// + /// In particular, it must not be used with handles to open registry + /// keys which need to be closed with [`RegCloseKey`] instead. + /// + /// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is + /// sometimes a valid handle value. See [here] for the full story. + /// + /// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey + /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 + #[inline] + unsafe fn from_raw_handle(handle: RawHandle) -> Self { + assert!(!handle.is_null()); + Self { handle: NonNull::new_unchecked(handle) } + } +} + +impl FromRawHandle for HandleOrInvalid { + /// Constructs a new instance of `Self` from the given `RawHandle` returned + /// from a Windows API that uses `INVALID_HANDLE_VALUE` to indicate + /// failure, such as `CreateFileW`. + /// + /// Use `Option<OwnedHandle>` instead of `HandleOrInvalid` for APIs that + /// use null to indicate failure. + /// + /// # Safety + /// + /// The resource pointed to by `handle` must be either open and otherwise + /// unowned, or equal to `INVALID_HANDLE_VALUE` (-1). It must not be null. + /// Note that not all Windows APIs use `INVALID_HANDLE_VALUE` for errors; + /// see [here] for the full story. + /// + /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 + #[inline] + unsafe fn from_raw_handle(handle: RawHandle) -> Self { + // We require non-null here to catch errors earlier. + Self(Some(OwnedHandle::from_raw_handle(handle))) + } +} + +impl Drop for OwnedHandle { + #[inline] + fn drop(&mut self) { + unsafe { + let _ = c::CloseHandle(self.handle.as_ptr()); + } + } +} + +impl fmt::Debug for BorrowedHandle<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BorrowedHandle").field("handle", &self.handle).finish() + } +} + +impl fmt::Debug for OwnedHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedHandle").field("handle", &self.handle).finish() + } +} + +/// A trait to borrow the handle from an underlying object. +#[unstable(feature = "io_safety", issue = "87074")] +pub trait AsHandle { + /// Borrows the handle. + /// + /// # Example + /// + /// ```rust,no_run + /// # #![feature(io_safety)] + /// use std::fs::File; + /// # use std::io; + /// use std::os::windows::io::{AsHandle, BorrowedHandle}; + /// + /// let mut f = File::open("foo.txt")?; + /// let borrowed_handle: BorrowedHandle<'_> = f.as_handle(); + /// # Ok::<(), io::Error>(()) + /// ``` + fn as_handle(&self) -> BorrowedHandle<'_>; +} + +impl AsHandle for BorrowedHandle<'_> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + *self + } +} + +impl AsHandle for OwnedHandle { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + // Safety: `OwnedHandle` and `BorrowedHandle` have the same validity + // invariants, and the `BorrowdHandle` is bounded by the lifetime + // of `&self`. + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl AsHandle for fs::File { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + self.as_inner().as_handle() + } +} + +impl From<fs::File> for OwnedHandle { + #[inline] + fn from(file: fs::File) -> OwnedHandle { + file.into_inner().into_inner().into_inner().into() + } +} + +impl From<OwnedHandle> for fs::File { + #[inline] + fn from(owned: OwnedHandle) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned))) + } +} + +impl AsHandle for crate::io::Stdin { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl<'a> AsHandle for crate::io::StdinLock<'a> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl AsHandle for crate::io::Stdout { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl<'a> AsHandle for crate::io::StdoutLock<'a> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl AsHandle for crate::io::Stderr { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl<'a> AsHandle for crate::io::StderrLock<'a> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl AsHandle for crate::process::ChildStdin { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl From<crate::process::ChildStdin> for OwnedHandle { + #[inline] + fn from(child_stdin: crate::process::ChildStdin) -> OwnedHandle { + unsafe { OwnedHandle::from_raw_handle(child_stdin.into_raw_handle()) } + } +} + +impl AsHandle for crate::process::ChildStdout { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl From<crate::process::ChildStdout> for OwnedHandle { + #[inline] + fn from(child_stdout: crate::process::ChildStdout) -> OwnedHandle { + unsafe { OwnedHandle::from_raw_handle(child_stdout.into_raw_handle()) } + } +} + +impl AsHandle for crate::process::ChildStderr { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl From<crate::process::ChildStderr> for OwnedHandle { + #[inline] + fn from(child_stderr: crate::process::ChildStderr) -> OwnedHandle { + unsafe { OwnedHandle::from_raw_handle(child_stderr.into_raw_handle()) } + } +} + +impl<T> AsHandle for crate::thread::JoinHandle<T> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl<T> From<crate::thread::JoinHandle<T>> for OwnedHandle { + #[inline] + fn from(join_handle: crate::thread::JoinHandle<T>) -> OwnedHandle { + join_handle.into_inner().into_handle().into_inner() + } +} diff --git a/library/std/src/os/windows/io/mod.rs b/library/std/src/os/windows/io/mod.rs new file mode 100644 index 00000000000..2f6f0769548 --- /dev/null +++ b/library/std/src/os/windows/io/mod.rs @@ -0,0 +1,56 @@ +//! Windows-specific extensions to general I/O primitives. +//! +//! Just like raw pointers, raw Windows handles and sockets point to resources +//! with dynamic lifetimes, and they can dangle if they outlive their resources +//! or be forged if they're created from invalid values. +//! +//! This module provides three types for representing raw handles and sockets +//! with different ownership properties: raw, borrowed, and owned, which are +//! analogous to types used for representing pointers: +//! +//! | Type | Analogous to | +//! | ---------------------- | ------------ | +//! | [`RawHandle`] | `*const _` | +//! | [`RawSocket`] | `*const _` | +//! | | | +//! | [`BorrowedHandle<'a>`] | `&'a _` | +//! | [`BorrowedSocket<'a>`] | `&'a _` | +//! | | | +//! | [`OwnedHandle`] | `Box<_>` | +//! | [`OwnedSocket`] | `Box<_>` | +//! +//! Like raw pointers, `RawHandle` and `RawSocket` values are primitive values. +//! And in new code, they should be considered unsafe to do I/O on (analogous +//! to dereferencing them). Rust did not always provide this guidance, so +//! existing code in the Rust ecosystem often doesn't mark `RawHandle` and +//! `RawSocket` usage as unsafe. Once the `io_safety` feature is stable, +//! libraries will be encouraged to migrate, either by adding `unsafe` to APIs +//! that dereference `RawHandle` and `RawSocket` values, or by using to +//! `BorrowedHandle`, `BorrowedSocket`, `OwnedHandle`, or `OwnedSocket`. +//! +//! Like references, `BorrowedHandle` and `BorrowedSocket` values are tied to a +//! lifetime, to ensure that they don't outlive the resource they point to. +//! These are safe to use. `BorrowedHandle` and `BorrowedSocket` values may be +//! used in APIs which provide safe access to any system call except for +//! `CloseHandle`, `closesocket`, or any other call that would end the +//! dynamic lifetime of the resource without ending the lifetime of the +//! handle or socket. +//! +//! Like boxes, `OwnedHandle` and `OwnedSocket` values conceptually own the +//! resource they point to, and free (close) it when they are dropped. +//! +//! [`BorrowedHandle<'a>`]: crate::os::windows::io::BorrowedHandle +//! [`BorrowedSocket<'a>`]: crate::os::windows::io::BorrowedSocket + +#![stable(feature = "rust1", since = "1.0.0")] + +mod handle; +mod raw; +mod socket; + +#[unstable(feature = "io_safety", issue = "87074")] +pub use handle::*; +#[stable(feature = "rust1", since = "1.0.0")] +pub use raw::*; +#[unstable(feature = "io_safety", issue = "87074")] +pub use socket::*; diff --git a/library/std/src/os/windows/io.rs b/library/std/src/os/windows/io/raw.rs index 31b5d015ed0..c7f122048a1 100644 --- a/library/std/src/os/windows/io.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -5,6 +5,7 @@ use crate::fs; use crate::io; use crate::net; +use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; use crate::sys; use crate::sys::c; @@ -61,7 +62,7 @@ pub trait IntoRawHandle { impl AsRawHandle for fs::File { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as RawHandle + self.as_inner().as_raw_handle() as RawHandle } } @@ -112,7 +113,9 @@ impl FromRawHandle for fs::File { #[inline] unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { let handle = handle as c::HANDLE; - fs::File::from_inner(sys::fs::File::from_inner(handle)) + fs::File::from_inner(sys::fs::File::from_inner(FromInner::from_inner( + OwnedHandle::from_raw_handle(handle), + ))) } } @@ -120,7 +123,7 @@ impl FromRawHandle for fs::File { impl IntoRawHandle for fs::File { #[inline] fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_raw_handle() as *mut _ } } @@ -166,21 +169,21 @@ pub trait IntoRawSocket { impl AsRawSocket for net::TcpStream { #[inline] fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() + self.as_inner().socket().as_raw_socket() } } #[stable(feature = "rust1", since = "1.0.0")] impl AsRawSocket for net::TcpListener { #[inline] fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() + self.as_inner().socket().as_raw_socket() } } #[stable(feature = "rust1", since = "1.0.0")] impl AsRawSocket for net::UdpSocket { #[inline] fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() + self.as_inner().socket().as_raw_socket() } } @@ -188,7 +191,7 @@ impl AsRawSocket for net::UdpSocket { impl FromRawSocket for net::TcpStream { #[inline] unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { - let sock = sys::net::Socket::from_inner(sock); + let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) } } @@ -196,7 +199,7 @@ impl FromRawSocket for net::TcpStream { impl FromRawSocket for net::TcpListener { #[inline] unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { - let sock = sys::net::Socket::from_inner(sock); + let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) } } @@ -204,7 +207,7 @@ impl FromRawSocket for net::TcpListener { impl FromRawSocket for net::UdpSocket { #[inline] unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { - let sock = sys::net::Socket::from_inner(sock); + let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) } } @@ -213,7 +216,7 @@ impl FromRawSocket for net::UdpSocket { impl IntoRawSocket for net::TcpStream { #[inline] fn into_raw_socket(self) -> RawSocket { - self.into_inner().into_socket().into_inner() + self.into_inner().into_socket().into_inner().into_raw_socket() } } @@ -221,7 +224,7 @@ impl IntoRawSocket for net::TcpStream { impl IntoRawSocket for net::TcpListener { #[inline] fn into_raw_socket(self) -> RawSocket { - self.into_inner().into_socket().into_inner() + self.into_inner().into_socket().into_inner().into_raw_socket() } } @@ -229,6 +232,6 @@ impl IntoRawSocket for net::TcpListener { impl IntoRawSocket for net::UdpSocket { #[inline] fn into_raw_socket(self) -> RawSocket { - self.into_inner().into_socket().into_inner() + self.into_inner().into_socket().into_inner().into_raw_socket() } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs new file mode 100644 index 00000000000..23db66df09f --- /dev/null +++ b/library/std/src/os/windows/io/socket.rs @@ -0,0 +1,216 @@ +//! Owned and borrowed OS sockets. + +#![unstable(feature = "io_safety", issue = "87074")] + +use super::raw::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; +use crate::fmt; +use crate::marker::PhantomData; +use crate::mem::forget; +use crate::sys::c; + +/// A borrowed socket. +/// +/// This has a lifetime parameter to tie it to the lifetime of something that +/// owns the socket. +/// +/// This uses `repr(transparent)` and has the representation of a host socket, +/// so it can be used in FFI in places where a socket is passed as an argument, +/// it is not captured or consumed, and it never has the value +/// `INVALID_SOCKET`. +#[derive(Copy, Clone)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `INVALID_SOCKET`. +#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] +#[cfg_attr( + target_pointer_width = "64", + rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) +)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct BorrowedSocket<'socket> { + socket: RawSocket, + _phantom: PhantomData<&'socket OwnedSocket>, +} + +/// An owned socket. +/// +/// This closes the socket on drop. +/// +/// This uses `repr(transparent)` and has the representation of a host socket, +/// so it can be used in FFI in places where a socket is passed as a consumed +/// argument or returned as an owned value, and it never has the value +/// `INVALID_SOCKET`. +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `INVALID_SOCKET`. +#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] +#[cfg_attr( + target_pointer_width = "64", + rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) +)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct OwnedSocket { + socket: RawSocket, +} + +impl BorrowedSocket<'_> { + /// Return a `BorrowedSocket` holding the given raw socket. + /// + /// # Safety + /// + /// The resource pointed to by `raw` must remain open for the duration of + /// the returned `BorrowedSocket`, and it must not have the value + /// `INVALID_SOCKET`. + #[inline] + #[unstable(feature = "io_safety", issue = "87074")] + pub unsafe fn borrow_raw_socket(socket: RawSocket) -> Self { + debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket); + Self { socket, _phantom: PhantomData } + } +} + +impl AsRawSocket for BorrowedSocket<'_> { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.socket + } +} + +impl AsRawSocket for OwnedSocket { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.socket + } +} + +impl IntoRawSocket for OwnedSocket { + #[inline] + fn into_raw_socket(self) -> RawSocket { + let socket = self.socket; + forget(self); + socket + } +} + +impl FromRawSocket for OwnedSocket { + /// Constructs a new instance of `Self` from the given raw socket. + /// + /// # Safety + /// + /// The resource pointed to by `socket` must be open and suitable for + /// assuming ownership. The resource must not require cleanup other than + /// `closesocket`. + #[inline] + unsafe fn from_raw_socket(socket: RawSocket) -> Self { + debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket); + Self { socket } + } +} + +impl Drop for OwnedSocket { + #[inline] + fn drop(&mut self) { + unsafe { + let _ = c::closesocket(self.socket); + } + } +} + +impl fmt::Debug for BorrowedSocket<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BorrowedSocket").field("socket", &self.socket).finish() + } +} + +impl fmt::Debug for OwnedSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedSocket").field("socket", &self.socket).finish() + } +} + +/// A trait to borrow the socket from an underlying object. +#[unstable(feature = "io_safety", issue = "87074")] +pub trait AsSocket { + /// Borrows the socket. + fn as_socket(&self) -> BorrowedSocket<'_>; +} + +impl AsSocket for BorrowedSocket<'_> { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + *self + } +} + +impl AsSocket for OwnedSocket { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + // Safety: `OwnedSocket` and `BorrowedSocket` have the same validity + // invariants, and the `BorrowdSocket` is bounded by the lifetime + // of `&self`. + unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + } +} + +impl AsSocket for crate::net::TcpStream { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + } +} + +impl From<crate::net::TcpStream> for OwnedSocket { + #[inline] + fn from(tcp_stream: crate::net::TcpStream) -> OwnedSocket { + unsafe { OwnedSocket::from_raw_socket(tcp_stream.into_raw_socket()) } + } +} + +impl From<OwnedSocket> for crate::net::TcpStream { + #[inline] + fn from(owned: OwnedSocket) -> Self { + unsafe { Self::from_raw_socket(owned.into_raw_socket()) } + } +} + +impl AsSocket for crate::net::TcpListener { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + } +} + +impl From<crate::net::TcpListener> for OwnedSocket { + #[inline] + fn from(tcp_listener: crate::net::TcpListener) -> OwnedSocket { + unsafe { OwnedSocket::from_raw_socket(tcp_listener.into_raw_socket()) } + } +} + +impl From<OwnedSocket> for crate::net::TcpListener { + #[inline] + fn from(owned: OwnedSocket) -> Self { + unsafe { Self::from_raw_socket(owned.into_raw_socket()) } + } +} + +impl AsSocket for crate::net::UdpSocket { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + } +} + +impl From<crate::net::UdpSocket> for OwnedSocket { + #[inline] + fn from(udp_socket: crate::net::UdpSocket) -> OwnedSocket { + unsafe { OwnedSocket::from_raw_socket(udp_socket.into_raw_socket()) } + } +} + +impl From<OwnedSocket> for crate::net::UdpSocket { + #[inline] + fn from(owned: OwnedSocket) -> Self { + unsafe { Self::from_raw_socket(owned.into_raw_socket()) } + } +} diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs index 52ac508f9f7..969054dd3b3 100644 --- a/library/std/src/os/windows/mod.rs +++ b/library/std/src/os/windows/mod.rs @@ -32,8 +32,11 @@ pub mod prelude { pub use super::fs::{MetadataExt, OpenOptionsExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{AsRawHandle, AsRawSocket, RawHandle, RawSocket}; + pub use super::io::{ + AsHandle, AsSocket, BorrowedHandle, BorrowedSocket, FromRawHandle, FromRawSocket, + HandleOrInvalid, IntoRawHandle, IntoRawSocket, OwnedHandle, OwnedSocket, + }; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{FromRawHandle, FromRawSocket, IntoRawHandle, IntoRawSocket}; + pub use super::io::{AsRawHandle, AsRawSocket, RawHandle, RawSocket}; } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 9e7ccd015b6..b246599dfc0 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -3,7 +3,9 @@ #![stable(feature = "process_extensions", since = "1.2.0")] use crate::ffi::OsStr; -use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; +use crate::os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, +}; use crate::process; use crate::sealed::Sealed; use crate::sys; @@ -12,7 +14,16 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawHandle for process::Stdio { unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio { - let handle = sys::handle::Handle::new(handle as *mut _); + let handle = sys::handle::Handle::from_raw_handle(handle as *mut _); + let io = sys::process::Stdio::Handle(handle); + process::Stdio::from_inner(io) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<OwnedHandle> for process::Stdio { + fn from(handle: OwnedHandle) -> process::Stdio { + let handle = sys::handle::Handle::from_inner(handle); let io = sys::process::Stdio::Handle(handle); process::Stdio::from_inner(io) } @@ -22,14 +33,29 @@ impl FromRawHandle for process::Stdio { impl AsRawHandle for process::Child { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsHandle for process::Child { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + self.as_inner().handle().as_handle() } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::Child { fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From<process::Child> for OwnedHandle { + fn from(child: process::Child) -> OwnedHandle { + child.into_inner().into_handle().into_inner() } } @@ -37,7 +63,7 @@ impl IntoRawHandle for process::Child { impl AsRawHandle for process::ChildStdin { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ } } @@ -45,7 +71,7 @@ impl AsRawHandle for process::ChildStdin { impl AsRawHandle for process::ChildStdout { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ } } @@ -53,28 +79,28 @@ impl AsRawHandle for process::ChildStdout { impl AsRawHandle for process::ChildStderr { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::ChildStdin { fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::ChildStdout { fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::ChildStderr { fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ } } diff --git a/library/std/src/os/windows/thread.rs b/library/std/src/os/windows/thread.rs index 6bd02054f71..fb1bf66ceed 100644 --- a/library/std/src/os/windows/thread.rs +++ b/library/std/src/os/windows/thread.rs @@ -10,7 +10,7 @@ use crate::thread; impl<T> AsRawHandle for thread::JoinHandle<T> { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ } } @@ -18,6 +18,6 @@ impl<T> AsRawHandle for thread::JoinHandle<T> { impl<T> IntoRawHandle for thread::JoinHandle<T> { #[inline] fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ } } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 69419145b1b..2a9c361c18a 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -315,7 +315,7 @@ fn has_physical_root(s: &[u8], prefix: Option<Prefix<'_>>) -> bool { } // basic workhorse for splitting stem and extension -fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { +fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { if os_str_as_u8_slice(file) == b".." { return (Some(file), None); } @@ -334,6 +334,25 @@ fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { } } +fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { + let slice = os_str_as_u8_slice(file); + if slice == b".." { + return (file, None); + } + + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + let i = match slice[1..].iter().position(|b| *b == b'.') { + Some(i) => i + 1, + None => return (file, None), + }; + let before = &slice[..i]; + let after = &slice[i + 1..]; + unsafe { (u8_slice_as_os_str(before), Some(u8_slice_as_os_str(after))) } +} + //////////////////////////////////////////////////////////////////////////////// // The core iterators //////////////////////////////////////////////////////////////////////////////// @@ -962,7 +981,7 @@ impl cmp::Eq for Components<'_> {} impl<'a> cmp::PartialOrd for Components<'a> { #[inline] fn partial_cmp(&self, other: &Components<'a>) -> Option<cmp::Ordering> { - Iterator::partial_cmp(self.clone(), other.clone()) + Some(compare_components(self.clone(), other.clone())) } } @@ -970,8 +989,41 @@ impl<'a> cmp::PartialOrd for Components<'a> { impl cmp::Ord for Components<'_> { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { - Iterator::cmp(self.clone(), other.clone()) + compare_components(self.clone(), other.clone()) + } +} + +fn compare_components(mut left: Components<'_>, mut right: Components<'_>) -> cmp::Ordering { + // Fast path for long shared prefixes + // + // - compare raw bytes to find first mismatch + // - backtrack to find separator before mismatch to avoid ambiguous parsings of '.' or '..' characters + // - if found update state to only do a component-wise comparison on the remainder, + // otherwise do it on the full path + // + // The fast path isn't taken for paths with a PrefixComponent to avoid backtracking into + // the middle of one + if left.prefix.is_none() && right.prefix.is_none() && left.front == right.front { + // this might benefit from a [u8]::first_mismatch simd implementation, if it existed + let first_difference = + match left.path.iter().zip(right.path.iter()).position(|(&a, &b)| a != b) { + None if left.path.len() == right.path.len() => return cmp::Ordering::Equal, + None => left.path.len().min(right.path.len()), + Some(diff) => diff, + }; + + if let Some(previous_sep) = + left.path[..first_difference].iter().rposition(|&b| left.is_sep_byte(b)) + { + let mismatched_component_start = previous_sep + 1; + left.path = &left.path[mismatched_component_start..]; + left.front = State::Body; + right.path = &right.path[mismatched_component_start..]; + right.front = State::Body; + } } + + Iterator::cmp(left, right) } /// An iterator over [`Path`] and its ancestors. @@ -1704,7 +1756,7 @@ impl cmp::Eq for PathBuf {} impl cmp::PartialOrd for PathBuf { #[inline] fn partial_cmp(&self, other: &PathBuf) -> Option<cmp::Ordering> { - self.components().partial_cmp(other.components()) + Some(compare_components(self.components(), other.components())) } } @@ -1712,7 +1764,7 @@ impl cmp::PartialOrd for PathBuf { impl cmp::Ord for PathBuf { #[inline] fn cmp(&self, other: &PathBuf) -> cmp::Ordering { - self.components().cmp(other.components()) + compare_components(self.components(), other.components()) } } @@ -2180,9 +2232,49 @@ impl Path { /// assert_eq!("foo", Path::new("foo.rs").file_stem().unwrap()); /// assert_eq!("foo.tar", Path::new("foo.tar.gz").file_stem().unwrap()); /// ``` + /// + /// # See Also + /// This method is similar to [`Path::file_prefix`], which extracts the portion of the file name + /// before the *first* `.` + /// + /// [`Path::file_prefix`]: Path::file_prefix + /// #[stable(feature = "rust1", since = "1.0.0")] pub fn file_stem(&self) -> Option<&OsStr> { - self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after)) + self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.or(after)) + } + + /// Extracts the prefix of [`self.file_name`]. + /// + /// The prefix is: + /// + /// * [`None`], if there is no file name; + /// * The entire file name if there is no embedded `.`; + /// * The portion of the file name before the first non-beginning `.`; + /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * The portion of the file name before the second `.` if the file name begins with `.` + /// + /// [`self.file_name`]: Path::file_name + /// + /// # Examples + /// + /// ``` + /// # #![feature(path_file_prefix)] + /// use std::path::Path; + /// + /// assert_eq!("foo", Path::new("foo.rs").file_prefix().unwrap()); + /// assert_eq!("foo", Path::new("foo.tar.gz").file_prefix().unwrap()); + /// ``` + /// + /// # See Also + /// This method is similar to [`Path::file_stem`], which extracts the portion of the file name + /// before the *last* `.` + /// + /// [`Path::file_stem`]: Path::file_stem + /// + #[unstable(feature = "path_file_prefix", issue = "86319")] + pub fn file_prefix(&self) -> Option<&OsStr> { + self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before)) } /// Extracts the extension of [`self.file_name`], if possible. @@ -2206,7 +2298,7 @@ impl Path { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn extension(&self) -> Option<&OsStr> { - self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after)) + self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.and(after)) } /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. @@ -2686,7 +2778,7 @@ impl fmt::Display for Display<'_> { impl cmp::PartialEq for Path { #[inline] fn eq(&self, other: &Path) -> bool { - self.components().eq(other.components()) + self.components() == other.components() } } @@ -2706,7 +2798,7 @@ impl cmp::Eq for Path {} impl cmp::PartialOrd for Path { #[inline] fn partial_cmp(&self, other: &Path) -> Option<cmp::Ordering> { - self.components().partial_cmp(other.components()) + Some(compare_components(self.components(), other.components())) } } @@ -2714,7 +2806,7 @@ impl cmp::PartialOrd for Path { impl cmp::Ord for Path { #[inline] fn cmp(&self, other: &Path) -> cmp::Ordering { - self.components().cmp(other.components()) + compare_components(self.components(), other.components()) } } diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 896d6c2a64c..ce23cf6cd21 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1,9 +1,11 @@ use super::*; +use crate::collections::BTreeSet; use crate::rc::Rc; use crate::sync::Arc; +use core::hint::black_box; -macro_rules! t( +macro_rules! t ( ($path:expr, iter: $iter:expr) => ( { let path = Path::new($path); @@ -73,15 +75,33 @@ macro_rules! t( } ); + ($path:expr, file_prefix: $file_prefix:expr, extension: $extension:expr) => ( + { + let path = Path::new($path); + + let prefix = path.file_prefix().map(|p| p.to_str().unwrap()); + let exp_prefix: Option<&str> = $file_prefix; + assert!(prefix == exp_prefix, "file_prefix: Expected {:?}, found {:?}", + exp_prefix, prefix); + + let ext = path.extension().map(|p| p.to_str().unwrap()); + let exp_ext: Option<&str> = $extension; + assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", + exp_ext, ext); + } + ); + ($path:expr, iter: $iter:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr, parent: $parent:expr, file_name: $file:expr, - file_stem: $file_stem:expr, extension: $extension:expr) => ( + file_stem: $file_stem:expr, extension: $extension:expr, + file_prefix: $file_prefix:expr) => ( { t!($path, iter: $iter); t!($path, has_root: $has_root, is_absolute: $is_absolute); t!($path, parent: $parent, file_name: $file); t!($path, file_stem: $file_stem, extension: $extension); + t!($path, file_prefix: $file_prefix, extension: $extension); } ); ); @@ -116,7 +136,8 @@ pub fn test_decompositions_unix() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo", @@ -126,7 +147,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/", @@ -136,7 +158,8 @@ pub fn test_decompositions_unix() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/foo", @@ -146,7 +169,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/", @@ -156,7 +180,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/foo/", @@ -166,7 +191,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/bar", @@ -176,7 +202,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("/foo/bar", @@ -186,7 +213,8 @@ pub fn test_decompositions_unix() { parent: Some("/foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("///foo///", @@ -196,7 +224,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("///foo///bar", @@ -206,7 +235,8 @@ pub fn test_decompositions_unix() { parent: Some("///foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./.", @@ -216,7 +246,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/..", @@ -226,7 +257,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("../", @@ -236,7 +268,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/.", @@ -246,7 +279,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/..", @@ -256,7 +290,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/./", @@ -266,7 +301,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/./bar", @@ -276,7 +312,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("foo/../", @@ -286,7 +323,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/../bar", @@ -296,7 +334,8 @@ pub fn test_decompositions_unix() { parent: Some("foo/.."), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./a", @@ -306,7 +345,8 @@ pub fn test_decompositions_unix() { parent: Some("."), file_name: Some("a"), file_stem: Some("a"), - extension: None + extension: None, + file_prefix: Some("a") ); t!(".", @@ -316,7 +356,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("./", @@ -326,7 +367,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("a/b", @@ -336,7 +378,8 @@ pub fn test_decompositions_unix() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a//b", @@ -346,7 +389,8 @@ pub fn test_decompositions_unix() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/./b", @@ -356,7 +400,8 @@ pub fn test_decompositions_unix() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/b/c", @@ -366,7 +411,8 @@ pub fn test_decompositions_unix() { parent: Some("a/b"), file_name: Some("c"), file_stem: Some("c"), - extension: None + extension: None, + file_prefix: Some("c") ); t!(".foo", @@ -376,7 +422,41 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some(".foo"), file_stem: Some(".foo"), - extension: None + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.foo", + iter: ["a", ".foo"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.rustfmt.toml", + iter: ["a", ".rustfmt.toml"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".rustfmt.toml"), + file_stem: Some(".rustfmt"), + extension: Some("toml"), + file_prefix: Some(".rustfmt") + ); + + t!("a/.x.y.z", + iter: ["a", ".x.y.z"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".x.y.z"), + file_stem: Some(".x.y"), + extension: Some("z"), + file_prefix: Some(".x") ); } @@ -390,7 +470,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo", @@ -400,7 +481,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/", @@ -410,7 +492,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\", @@ -420,7 +503,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:", @@ -430,7 +514,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:\\", @@ -440,7 +525,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:/", @@ -450,7 +536,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/foo", @@ -460,7 +547,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/", @@ -470,7 +558,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/foo/", @@ -480,7 +569,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/bar", @@ -490,7 +580,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("/foo/bar", @@ -500,7 +591,8 @@ pub fn test_decompositions_windows() { parent: Some("/foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("///foo///", @@ -510,7 +602,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("///foo///bar", @@ -520,7 +613,8 @@ pub fn test_decompositions_windows() { parent: Some("///foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./.", @@ -530,7 +624,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/..", @@ -540,7 +635,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("../", @@ -550,7 +646,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/.", @@ -560,7 +657,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/..", @@ -570,7 +668,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/./", @@ -580,7 +679,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/./bar", @@ -590,7 +690,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("foo/../", @@ -600,7 +701,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/../bar", @@ -610,7 +712,8 @@ pub fn test_decompositions_windows() { parent: Some("foo/.."), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./a", @@ -620,7 +723,8 @@ pub fn test_decompositions_windows() { parent: Some("."), file_name: Some("a"), file_stem: Some("a"), - extension: None + extension: None, + file_prefix: Some("a") ); t!(".", @@ -630,7 +734,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("./", @@ -640,7 +745,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("a/b", @@ -650,7 +756,8 @@ pub fn test_decompositions_windows() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a//b", @@ -660,7 +767,8 @@ pub fn test_decompositions_windows() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/./b", @@ -670,7 +778,8 @@ pub fn test_decompositions_windows() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/b/c", @@ -680,7 +789,9 @@ pub fn test_decompositions_windows() { parent: Some("a/b"), file_name: Some("c"), file_stem: Some("c"), - extension: None); + extension: None, + file_prefix: Some("c") + ); t!("a\\b\\c", iter: ["a", "b", "c"], @@ -689,7 +800,8 @@ pub fn test_decompositions_windows() { parent: Some("a\\b"), file_name: Some("c"), file_stem: Some("c"), - extension: None + extension: None, + file_prefix: Some("c") ); t!("\\a", @@ -699,7 +811,8 @@ pub fn test_decompositions_windows() { parent: Some("\\"), file_name: Some("a"), file_stem: Some("a"), - extension: None + extension: None, + file_prefix: Some("a") ); t!("c:\\foo.txt", @@ -709,7 +822,8 @@ pub fn test_decompositions_windows() { parent: Some("c:\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\server\\share\\foo.txt", @@ -719,7 +833,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\server\\share\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\server\\share", @@ -729,7 +844,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\server", @@ -739,7 +855,8 @@ pub fn test_decompositions_windows() { parent: Some("\\"), file_name: Some("server"), file_stem: Some("server"), - extension: None + extension: None, + file_prefix: Some("server") ); t!("\\\\?\\bar\\foo.txt", @@ -749,7 +866,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\bar\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\?\\bar", @@ -759,7 +877,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\", @@ -769,7 +888,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\UNC\\server\\share\\foo.txt", @@ -779,7 +899,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\UNC\\server\\share\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\?\\UNC\\server", @@ -789,7 +910,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\UNC\\", @@ -799,7 +921,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:\\foo.txt", @@ -809,7 +932,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\C:\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\?\\C:\\", @@ -819,7 +943,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:", @@ -829,7 +954,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\foo/bar", @@ -839,7 +965,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:/foo", @@ -849,7 +976,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\.\\foo\\bar", @@ -859,7 +987,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\.\\foo\\"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("\\\\.\\foo", @@ -869,7 +998,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\.\\foo/bar", @@ -879,7 +1009,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\.\\foo/"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("\\\\.\\foo\\bar/baz", @@ -889,7 +1020,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\.\\foo\\bar"), file_name: Some("baz"), file_stem: Some("baz"), - extension: None + extension: None, + file_prefix: Some("baz") ); t!("\\\\.\\", @@ -899,7 +1031,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\a\\b\\", @@ -909,7 +1042,52 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\a\\"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") + ); + + t!("\\\\?\\C:\\foo.txt.zip", + iter: ["\\\\?\\C:", "\\", "foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some("foo.txt.zip"), + file_stem: Some("foo.txt"), + extension: Some("zip"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\C:\\.foo.txt.zip", + iter: ["\\\\?\\C:", "\\", ".foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo.txt.zip"), + file_stem: Some(".foo.txt"), + extension: Some("zip"), + file_prefix: Some(".foo") + ); + + t!("\\\\?\\C:\\.foo", + iter: ["\\\\?\\C:", "\\", ".foo"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.x.y.z", + iter: ["a", ".x.y.z"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".x.y.z"), + file_stem: Some(".x.y"), + extension: Some("z"), + file_prefix: Some(".x") ); } @@ -949,12 +1127,59 @@ pub fn test_stem_ext() { t!("..", file_stem: None, extension: None); + t!(".x.y.z", file_stem: Some(".x.y"), extension: Some("z")); + + t!("..x.y.z", file_stem: Some("..x.y"), extension: Some("z")); + t!("", file_stem: None, extension: None); } #[test] +pub fn test_prefix_ext() { + t!("foo", + file_prefix: Some("foo"), + extension: None + ); + + t!("foo.", + file_prefix: Some("foo"), + extension: Some("") + ); + + t!(".foo", + file_prefix: Some(".foo"), + extension: None + ); + + t!("foo.txt", + file_prefix: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.txt", + file_prefix: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.", + file_prefix: Some("foo"), + extension: Some("") + ); + + t!(".", file_prefix: None, extension: None); + + t!("..", file_prefix: None, extension: None); + + t!(".x.y.z", file_prefix: Some(".x"), extension: Some("z")); + + t!("..x.y.z", file_prefix: Some("."), extension: Some("z")); + + t!("", file_prefix: None, extension: None); +} + +#[test] pub fn test_push() { - macro_rules! tp( + macro_rules! tp ( ($path:expr, $push:expr, $expected:expr) => ( { let mut actual = PathBuf::from($path); actual.push($push); @@ -1042,7 +1267,7 @@ pub fn test_push() { #[test] pub fn test_pop() { - macro_rules! tp( + macro_rules! tp ( ($path:expr, $expected:expr, $output:expr) => ( { let mut actual = PathBuf::from($path); let output = actual.pop(); @@ -1096,7 +1321,7 @@ pub fn test_pop() { #[test] pub fn test_set_file_name() { - macro_rules! tfn( + macro_rules! tfn ( ($path:expr, $file:expr, $expected:expr) => ( { let mut p = PathBuf::from($path); p.set_file_name($file); @@ -1130,7 +1355,7 @@ pub fn test_set_file_name() { #[test] pub fn test_set_extension() { - macro_rules! tfe( + macro_rules! tfe ( ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( { let mut p = PathBuf::from($path); let output = p.set_extension($ext); @@ -1192,7 +1417,7 @@ pub fn test_compare() { s.finish() } - macro_rules! tc( + macro_rules! tc ( ($path1:expr, $path2:expr, eq: $eq:expr, starts_with: $starts_with:expr, ends_with: $ends_with:expr, relative_from: $relative_from:expr) => ({ @@ -1392,3 +1617,69 @@ fn into_rc() { assert_eq!(&*rc2, path); assert_eq!(&*arc2, path); } + +#[test] +fn test_ord() { + macro_rules! ord( + ($ord:ident, $left:expr, $right:expr) => ( { + assert_eq!(Path::new($left).cmp(&Path::new($right)), core::cmp::Ordering::$ord); + }); + ); + + ord!(Less, "1", "2"); + ord!(Less, "/foo/bar", "/foo./bar"); + ord!(Less, "foo/bar", "foo/bar."); + ord!(Equal, "foo/./bar", "foo/bar/"); + ord!(Equal, "foo/bar", "foo/bar/"); + ord!(Equal, "foo/bar", "foo/bar/."); + ord!(Equal, "foo/bar", "foo/bar//"); +} + +#[bench] +fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { + let prefix = "my/home"; + let mut paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect(); + + paths.sort(); + + b.iter(|| { + black_box(paths.as_mut_slice()).sort_unstable(); + }); +} + +#[bench] +fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} + +#[bench] +fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { + let prefix = "my/home"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} diff --git a/library/std/src/process.rs b/library/std/src/process.rs index f5ce5210f81..c9b21fcf9c6 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -258,6 +258,12 @@ pub struct ChildStdin { inner: AnonPipe, } +// In addition to the `impl`s here, `ChildStdin` also has `impl`s for +// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From<OwnedHandle>`/`Into<OwnedHandle>` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + #[stable(feature = "process", since = "1.0.0")] impl Write for ChildStdin { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { @@ -335,6 +341,12 @@ pub struct ChildStdout { inner: AnonPipe, } +// In addition to the `impl`s here, `ChildStdout` also has `impl`s for +// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From<OwnedHandle>`/`Into<OwnedHandle>` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + #[stable(feature = "process", since = "1.0.0")] impl Read for ChildStdout { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { @@ -396,6 +408,12 @@ pub struct ChildStderr { inner: AnonPipe, } +// In addition to the `impl`s here, `ChildStderr` also has `impl`s for +// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From<OwnedHandle>`/`Into<OwnedHandle>` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + #[stable(feature = "process", since = "1.0.0")] impl Read for ChildStderr { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 28e32681e15..0956726084e 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -5,21 +5,14 @@ mod tests; use crate::cmp; use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; -use crate::mem; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::sys::cvt; -use crate::sys_common::AsInner; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use libc::{c_int, c_void}; #[derive(Debug)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] -pub struct FileDesc { - fd: c_int, -} +pub struct FileDesc(OwnedFd); // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, // with the man page quoting that if the count of bytes to read is @@ -67,26 +60,13 @@ const fn max_iov() -> usize { } impl FileDesc { - pub fn new(fd: c_int) -> FileDesc { - assert_ne!(fd, -1i32); - // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { FileDesc { fd } } - } - - pub fn raw(&self) -> c_int { - self.fd - } - - /// Extracts the actual file descriptor without closing it. - pub fn into_raw(self) -> c_int { - let fd = self.fd; - mem::forget(self); - fd - } - pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { let ret = cvt(unsafe { - libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT)) + libc::read( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), READ_LIMIT), + ) })?; Ok(ret as usize) } @@ -95,7 +75,7 @@ impl FileDesc { pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let ret = cvt(unsafe { libc::readv( - self.fd, + self.as_raw_fd(), bufs.as_ptr() as *const libc::iovec, cmp::min(bufs.len(), max_iov()) as c_int, ) @@ -138,7 +118,7 @@ impl FileDesc { unsafe { cvt_pread64( - self.fd, + self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT), offset as i64, @@ -149,7 +129,11 @@ impl FileDesc { pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let ret = cvt(unsafe { - libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT)) + libc::write( + self.as_raw_fd(), + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), READ_LIMIT), + ) })?; Ok(ret as usize) } @@ -158,7 +142,7 @@ impl FileDesc { pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let ret = cvt(unsafe { libc::writev( - self.fd, + self.as_raw_fd(), bufs.as_ptr() as *const libc::iovec, cmp::min(bufs.len(), max_iov()) as c_int, ) @@ -196,7 +180,7 @@ impl FileDesc { unsafe { cvt_pwrite64( - self.fd, + self.as_raw_fd(), buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT), offset as i64, @@ -207,7 +191,7 @@ impl FileDesc { #[cfg(target_os = "linux")] pub fn get_cloexec(&self) -> io::Result<bool> { - unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } + unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } } #[cfg(not(any( @@ -224,7 +208,7 @@ impl FileDesc { )))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { - cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; + cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?; Ok(()) } } @@ -242,10 +226,10 @@ impl FileDesc { ))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { - let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; + let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?; let new = previous | libc::FD_CLOEXEC; if new != previous { - cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; + cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?; } Ok(()) } @@ -261,7 +245,7 @@ impl FileDesc { pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { unsafe { let v = nonblocking as c_int; - cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?; + cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?; Ok(()) } } @@ -269,14 +253,14 @@ impl FileDesc { #[cfg(not(target_os = "linux"))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { unsafe { - let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?; + let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?; let new = if nonblocking { previous | libc::O_NONBLOCK } else { previous & !libc::O_NONBLOCK }; if new != previous { - cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; + cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?; } Ok(()) } @@ -296,8 +280,8 @@ impl FileDesc { #[cfg(target_os = "espidf")] let cmd = libc::F_DUPFD; - let fd = cvt(unsafe { libc::fcntl(self.raw(), cmd, 0) })?; - Ok(FileDesc::new(fd)) + let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?; + Ok(unsafe { FileDesc::from_raw_fd(fd) }) } } @@ -312,19 +296,44 @@ impl<'a> Read for &'a FileDesc { } } -impl AsInner<c_int> for FileDesc { - fn as_inner(&self) -> &c_int { - &self.fd +impl AsInner<OwnedFd> for FileDesc { + fn as_inner(&self) -> &OwnedFd { + &self.0 + } +} + +impl IntoInner<OwnedFd> for FileDesc { + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl FromInner<OwnedFd> for FileDesc { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self(owned_fd) + } +} + +impl AsFd for FileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for FileDesc { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for FileDesc { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() } } -impl Drop for FileDesc { - fn drop(&mut self) { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // opened after we closed ours. - let _ = unsafe { libc::close(self.fd) }; +impl FromRawFd for FileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) } } diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs index c9520485c3c..5d17e46786c 100644 --- a/library/std/src/sys/unix/fd/tests.rs +++ b/library/std/src/sys/unix/fd/tests.rs @@ -1,9 +1,10 @@ use super::{FileDesc, IoSlice}; +use crate::os::unix::io::FromRawFd; use core::mem::ManuallyDrop; #[test] fn limit_vector_count() { - let stdout = ManuallyDrop::new(unsafe { FileDesc { fd: 1 } }); + let stdout = ManuallyDrop::new(unsafe { FileDesc::from_raw_fd(1) }); let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>(); assert!(stdout.write_vectored(&bufs).is_ok()); } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index fd4defd72eb..6075eb5c7c5 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -4,13 +4,14 @@ use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; use crate::path::{Path, PathBuf}; use crate::ptr; use crate::sync::Arc; use crate::sys::fd::FileDesc; use crate::sys::time::SystemTime; use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{AsInner, FromInner}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[cfg(any( all(target_os = "linux", target_env = "gnu"), @@ -764,11 +765,11 @@ impl File { // However, since this is a variadic function, C integer promotion rules mean that on // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms). let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; - Ok(File(FileDesc::new(fd))) + Ok(File(unsafe { FileDesc::from_raw_fd(fd) })) } pub fn file_attr(&self) -> io::Result<FileAttr> { - let fd = self.0.raw(); + let fd = self.as_raw_fd(); cfg_has_statx! { if let Some(ret) = unsafe { try_statx( @@ -787,7 +788,7 @@ impl File { } pub fn fsync(&self) -> io::Result<()> { - cvt_r(|| unsafe { os_fsync(self.0.raw()) })?; + cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?; return Ok(()); #[cfg(any(target_os = "macos", target_os = "ios"))] @@ -801,7 +802,7 @@ impl File { } pub fn datasync(&self) -> io::Result<()> { - cvt_r(|| unsafe { os_datasync(self.0.raw()) })?; + cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; return Ok(()); #[cfg(any(target_os = "macos", target_os = "ios"))] @@ -834,14 +835,14 @@ impl File { pub fn truncate(&self, size: u64) -> io::Result<()> { #[cfg(target_os = "android")] - return crate::sys::android::ftruncate64(self.0.raw(), size); + return crate::sys::android::ftruncate64(self.as_raw_fd(), size); #[cfg(not(target_os = "android"))] { use crate::convert::TryInto; let size: off64_t = size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; - cvt_r(|| unsafe { ftruncate64(self.0.raw(), size) }).map(drop) + cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop) } } @@ -891,7 +892,7 @@ impl File { SeekFrom::End(off) => (libc::SEEK_END, off), SeekFrom::Current(off) => (libc::SEEK_CUR, off), }; - let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; + let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?; Ok(n as u64) } @@ -899,16 +900,8 @@ impl File { self.0.duplicate().map(File) } - pub fn fd(&self) -> &FileDesc { - &self.0 - } - - pub fn into_fd(self) -> FileDesc { - self.0 - } - pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?; + cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?; Ok(()) } } @@ -933,9 +926,51 @@ fn cstr(path: &Path) -> io::Result<CString> { Ok(CString::new(path.as_os_str().as_bytes())?) } -impl FromInner<c_int> for File { - fn from_inner(fd: c_int) -> File { - File(FileDesc::new(fd)) +impl AsInner<FileDesc> for File { + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl AsInnerMut<FileDesc> for File { + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl IntoInner<FileDesc> for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner<FileDesc> for File { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for File { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) } } @@ -1009,7 +1044,7 @@ impl fmt::Debug for File { None } - let fd = self.0.raw(); + let fd = self.as_raw_fd(); let mut b = f.debug_struct("File"); b.field("fd", &fd); if let Some(path) = get_path(fd) { diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 3f614fde08a..c2f5da1dbbb 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -3,6 +3,7 @@ use crate::ffi::CStr; use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Shutdown, SocketAddr}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::str; use crate::sys::fd::FileDesc; use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; @@ -74,10 +75,10 @@ impl Socket { // flag to atomically create the socket and set it as // CLOEXEC. On Linux this was added in 2.6.27. let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; - Ok(Socket(FileDesc::new(fd))) + Ok(Socket(FileDesc::from_raw_fd(fd))) } else { let fd = cvt(libc::socket(fam, ty, 0))?; - let fd = FileDesc::new(fd); + let fd = FileDesc::from_raw_fd(fd); fd.set_cloexec()?; let socket = Socket(fd); @@ -109,11 +110,11 @@ impl Socket { ))] { // Like above, set cloexec atomically cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; - Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))) + Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1])))) } else { cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; - let a = FileDesc::new(fds[0]); - let b = FileDesc::new(fds[1]); + let a = FileDesc::from_raw_fd(fds[0]); + let b = FileDesc::from_raw_fd(fds[1]); a.set_cloexec()?; b.set_cloexec()?; Ok((Socket(a), Socket(b))) @@ -131,7 +132,7 @@ impl Socket { self.set_nonblocking(true)?; let r = unsafe { let (addrp, len) = addr.into_inner(); - cvt(libc::connect(self.0.raw(), addrp, len)) + cvt(libc::connect(self.as_raw_fd(), addrp, len)) }; self.set_nonblocking(false)?; @@ -142,7 +143,7 @@ impl Socket { Err(e) => return Err(e), } - let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }; + let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { return Err(io::Error::new_const( @@ -212,15 +213,17 @@ impl Socket { target_os = "netbsd", target_os = "openbsd", ))] { - let fd = cvt_r(|| unsafe { - libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) - })?; - Ok(Socket(FileDesc::new(fd))) + unsafe { + let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; + Ok(Socket(FileDesc::from_raw_fd(fd))) + } } else { - let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; - let fd = FileDesc::new(fd); - fd.set_cloexec()?; - Ok(Socket(fd)) + unsafe { + let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?; + let fd = FileDesc::from_raw_fd(fd); + fd.set_cloexec()?; + Ok(Socket(fd)) + } } } } @@ -231,7 +234,7 @@ impl Socket { fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> { let ret = cvt(unsafe { - libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) + libc::recv(self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) })?; Ok(ret as usize) } @@ -263,7 +266,7 @@ impl Socket { let n = cvt(unsafe { libc::recvfrom( - self.0.raw(), + self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags, @@ -288,7 +291,7 @@ impl Socket { target_os = "openbsd", ))] pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> { - let n = cvt(unsafe { libc::recvmsg(self.0.raw(), msg, libc::MSG_CMSG_CLOEXEC) })?; + let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?; Ok(n as usize) } @@ -319,7 +322,7 @@ impl Socket { target_os = "openbsd", ))] pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> { - let n = cvt(unsafe { libc::sendmsg(self.0.raw(), msg, 0) })?; + let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; Ok(n as usize) } @@ -369,7 +372,7 @@ impl Socket { Shutdown::Read => libc::SHUT_RD, Shutdown::Both => libc::SHUT_RDWR, }; - cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; + cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?; Ok(()) } @@ -396,7 +399,7 @@ impl Socket { #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as libc::c_int; - cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop) + cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) } #[cfg(any(target_os = "solaris", target_os = "illumos"))] @@ -410,23 +413,52 @@ impl Socket { let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.as_raw_fd() + } +} + +impl AsInner<FileDesc> for Socket { + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner<FileDesc> for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner<FileDesc> for Socket { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } } -impl AsInner<c_int> for Socket { - fn as_inner(&self) -> &c_int { - self.0.as_inner() +impl AsRawFd for Socket { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() } } -impl FromInner<c_int> for Socket { - fn from_inner(fd: c_int) -> Socket { - Socket(FileDesc::new(fd)) +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() } } -impl IntoInner<c_int> for Socket { - fn into_inner(self) -> c_int { - self.0.into_raw() +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) } } diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs index 7ae37bdda70..a56c275c942 100644 --- a/library/std/src/sys/unix/pipe.rs +++ b/library/std/src/sys/unix/pipe.rs @@ -1,7 +1,9 @@ use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::{cvt, cvt_r}; +use crate::sys_common::IntoInner; //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes @@ -24,16 +26,20 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { target_os = "openbsd", target_os = "redox" ))] { - cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?; - Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))) + unsafe { + cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?; + Ok((AnonPipe(FileDesc::from_raw_fd(fds[0])), AnonPipe(FileDesc::from_raw_fd(fds[1])))) + } } else { - cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; - - let fd0 = FileDesc::new(fds[0]); - let fd1 = FileDesc::new(fds[1]); - fd0.set_cloexec()?; - fd1.set_cloexec()?; - Ok((AnonPipe(fd0), AnonPipe(fd1))) + unsafe { + cvt(libc::pipe(fds.as_mut_ptr()))?; + + let fd0 = FileDesc::from_raw_fd(fds[0]); + let fd1 = FileDesc::from_raw_fd(fds[1]); + fd0.set_cloexec()?; + fd1.set_cloexec()?; + Ok((AnonPipe(fd0), AnonPipe(fd1))) + } } } } @@ -64,11 +70,10 @@ impl AnonPipe { pub fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() } +} - pub fn fd(&self) -> &FileDesc { - &self.0 - } - pub fn into_fd(self) -> FileDesc { +impl IntoInner<FileDesc> for AnonPipe { + fn into_inner(self) -> FileDesc { self.0 } } @@ -76,15 +81,15 @@ impl AnonPipe { pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> io::Result<()> { // Set both pipes into nonblocking mode as we're gonna be reading from both // in the `select` loop below, and we wouldn't want one to block the other! - let p1 = p1.into_fd(); - let p2 = p2.into_fd(); + let p1 = p1.into_inner(); + let p2 = p2.into_inner(); p1.set_nonblocking(true)?; p2.set_nonblocking(true)?; let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; - fds[0].fd = p1.raw(); + fds[0].fd = p1.as_raw_fd(); fds[0].events = libc::POLLIN; - fds[1].fd = p2.raw(); + fds[1].fd = p2.as_raw_fd(); fds[1].events = libc::POLLIN; loop { // wait for either pipe to become readable using `poll` @@ -120,3 +125,27 @@ pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> } } } + +impl AsRawFd for AnonPipe { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl AsFd for AnonPipe { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl IntoRawFd for AnonPipe { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for AnonPipe { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) + } +} diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index a1972380a9f..7b261a302c3 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -13,6 +13,7 @@ use crate::sys::fd::FileDesc; use crate::sys::fs::File; use crate::sys::pipe::{self, AnonPipe}; use crate::sys_common::process::{CommandEnv, CommandEnvs}; +use crate::sys_common::IntoInner; #[cfg(not(target_os = "fuchsia"))] use crate::sys::fs::OpenOptions; @@ -388,17 +389,17 @@ impl Stdio { // stderr. No matter which we dup first, the second will get // overwritten prematurely. Stdio::Fd(ref fd) => { - if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { + if fd.as_raw_fd() >= 0 && fd.as_raw_fd() <= libc::STDERR_FILENO { Ok((ChildStdio::Owned(fd.duplicate()?), None)) } else { - Ok((ChildStdio::Explicit(fd.raw()), None)) + Ok((ChildStdio::Explicit(fd.as_raw_fd()), None)) } } Stdio::MakePipe => { let (reader, writer) = pipe::anon_pipe()?; let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; - Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) + Ok((ChildStdio::Owned(theirs.into_inner()), Some(ours))) } #[cfg(not(target_os = "fuchsia"))] @@ -408,7 +409,7 @@ impl Stdio { opts.write(!readable); let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) }; let fd = File::open_c(&path, &opts)?; - Ok((ChildStdio::Owned(fd.into_fd()), None)) + Ok((ChildStdio::Owned(fd.into_inner()), None)) } #[cfg(target_os = "fuchsia")] @@ -419,13 +420,13 @@ impl Stdio { impl From<AnonPipe> for Stdio { fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_fd()) + Stdio::Fd(pipe.into_inner()) } } impl From<File> for Stdio { fn from(file: File) -> Stdio { - Stdio::Fd(file.into_fd()) + Stdio::Fd(file.into_inner()) } } @@ -434,7 +435,7 @@ impl ChildStdio { match *self { ChildStdio::Inherit => None, ChildStdio::Explicit(fd) => Some(fd), - ChildStdio::Owned(ref fd) => Some(fd.raw()), + ChildStdio::Owned(ref fd) => Some(fd.as_raw_fd()), #[cfg(target_os = "fuchsia")] ChildStdio::Null => None, diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 4b210d6af13..12edf04a4e2 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -97,7 +97,9 @@ impl Command { drop(env_lock); drop(output); - let mut p = Process::new(pid, pidfd); + // Safety: We obtained the pidfd from calling `clone3` with + // `CLONE_PIDFD` so it's valid an otherwise unowned. + let mut p = unsafe { Process::new(pid, pidfd) }; let mut bytes = [0; 8]; // loop to handle EINTR @@ -446,7 +448,8 @@ impl Command { None => None, }; - let mut p = Process::new(0, -1); + // Safety: -1 indicates we don't have a pidfd. + let mut p = unsafe { Process::new(0, -1) }; struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit<libc::posix_spawn_file_actions_t>); @@ -545,14 +548,17 @@ pub struct Process { impl Process { #[cfg(target_os = "linux")] - fn new(pid: pid_t, pidfd: pid_t) -> Self { + unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self { + use crate::os::unix::io::FromRawFd; use crate::sys_common::FromInner; - let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::new(pidfd))); + // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned. + let pidfd = (pidfd >= 0) + .then(|| PidFd::from_inner(unsafe { sys::fd::FileDesc::from_raw_fd(pidfd) })); Process { pid, status: None, pidfd } } #[cfg(not(target_os = "linux"))] - fn new(pid: pid_t, _pidfd: pid_t) -> Self { + unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self { Process { pid, status: None } } diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index a05fe8165cf..b359987595d 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -1,5 +1,6 @@ use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem::ManuallyDrop; +use crate::os::unix::io::{AsFd, BorrowedFd, FromRawFd}; use crate::sys::fd::FileDesc; pub struct Stdin(()); @@ -14,11 +15,11 @@ impl Stdin { impl io::Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read(buf) + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) } } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs) + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_vectored(bufs) } } #[inline] @@ -35,11 +36,13 @@ impl Stdout { impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write(buf) + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write(buf) } } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { - ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs) + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write_vectored(bufs) + } } #[inline] @@ -60,11 +63,13 @@ impl Stderr { impl io::Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write(buf) + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write(buf) } } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { - ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs) + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write_vectored(bufs) + } } #[inline] @@ -86,3 +91,51 @@ pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option<impl io::Write> { Some(Stderr::new()) } + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for io::Stdin { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl<'a> AsFd for io::StdinLock<'a> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for io::Stdout { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl<'a> AsFd for io::StdoutLock<'a> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for io::Stderr { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl<'a> AsFd for io::StderrLock<'a> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) } + } +} diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs index 1f6ea8d6e8d..e4f4456611c 100644 --- a/library/std/src/sys/wasi/fd.rs +++ b/library/std/src/sys/wasi/fd.rs @@ -5,11 +5,12 @@ use super::err2io; use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; use crate::net::Shutdown; -use crate::os::raw::c_int; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[derive(Debug)] pub struct WasiFd { - fd: c_int, + fd: OwnedFd, } fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] { @@ -27,38 +28,26 @@ fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { } impl WasiFd { - pub unsafe fn from_raw(fd: c_int) -> WasiFd { - WasiFd { fd } - } - - pub fn into_raw(self) -> c_int { - let ret = self.fd; - mem::forget(self); - ret - } - - pub fn as_raw(&self) -> c_int { - self.fd - } - pub fn datasync(&self) -> io::Result<()> { - unsafe { wasi::fd_datasync(self.fd as wasi::Fd).map_err(err2io) } + unsafe { wasi::fd_datasync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } } pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { - unsafe { wasi::fd_pread(self.fd as wasi::Fd, iovec(bufs), offset).map_err(err2io) } + unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, iovec(bufs), offset).map_err(err2io) } } pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { - unsafe { wasi::fd_pwrite(self.fd as wasi::Fd, ciovec(bufs), offset).map_err(err2io) } + unsafe { + wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, ciovec(bufs), offset).map_err(err2io) + } } pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - unsafe { wasi::fd_read(self.fd as wasi::Fd, iovec(bufs)).map_err(err2io) } + unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) } } pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { - unsafe { wasi::fd_write(self.fd as wasi::Fd, ciovec(bufs)).map_err(err2io) } + unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) } } pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { @@ -67,37 +56,42 @@ impl WasiFd { SeekFrom::End(pos) => (wasi::WHENCE_END, pos), SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos), }; - unsafe { wasi::fd_seek(self.fd as wasi::Fd, offset, whence).map_err(err2io) } + unsafe { wasi::fd_seek(self.as_raw_fd() as wasi::Fd, offset, whence).map_err(err2io) } } pub fn tell(&self) -> io::Result<u64> { - unsafe { wasi::fd_tell(self.fd as wasi::Fd).map_err(err2io) } + unsafe { wasi::fd_tell(self.as_raw_fd() as wasi::Fd).map_err(err2io) } } // FIXME: __wasi_fd_fdstat_get pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> { - unsafe { wasi::fd_fdstat_set_flags(self.fd as wasi::Fd, flags).map_err(err2io) } + unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } } pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> { - unsafe { wasi::fd_fdstat_set_rights(self.fd as wasi::Fd, base, inheriting).map_err(err2io) } + unsafe { + wasi::fd_fdstat_set_rights(self.as_raw_fd() as wasi::Fd, base, inheriting) + .map_err(err2io) + } } pub fn sync(&self) -> io::Result<()> { - unsafe { wasi::fd_sync(self.fd as wasi::Fd).map_err(err2io) } + unsafe { wasi::fd_sync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } } pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { - unsafe { wasi::fd_advise(self.fd as wasi::Fd, offset, len, advice).map_err(err2io) } + unsafe { + wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io) + } } pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - unsafe { wasi::fd_allocate(self.fd as wasi::Fd, offset, len).map_err(err2io) } + unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) } } pub fn create_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_create_directory(self.fd as wasi::Fd, path).map_err(err2io) } + unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } pub fn link( @@ -109,10 +103,10 @@ impl WasiFd { ) -> io::Result<()> { unsafe { wasi::path_link( - self.fd as wasi::Fd, + self.as_raw_fd() as wasi::Fd, old_flags, old_path, - new_fd.fd as wasi::Fd, + new_fd.as_raw_fd() as wasi::Fd, new_path, ) .map_err(err2io) @@ -130,7 +124,7 @@ impl WasiFd { ) -> io::Result<WasiFd> { unsafe { wasi::path_open( - self.fd as wasi::Fd, + self.as_raw_fd() as wasi::Fd, dirflags, path, oflags, @@ -138,34 +132,39 @@ impl WasiFd { fs_rights_inheriting, fs_flags, ) - .map(|fd| WasiFd::from_raw(fd as c_int)) + .map(|fd| WasiFd::from_raw_fd(fd as RawFd)) .map_err(err2io) } } pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result<usize> { unsafe { - wasi::fd_readdir(self.fd as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie) + wasi::fd_readdir(self.as_raw_fd() as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie) .map_err(err2io) } } pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result<usize> { unsafe { - wasi::path_readlink(self.fd as wasi::Fd, path, buf.as_mut_ptr(), buf.len()) + wasi::path_readlink(self.as_raw_fd() as wasi::Fd, path, buf.as_mut_ptr(), buf.len()) .map_err(err2io) } } pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> { unsafe { - wasi::path_rename(self.fd as wasi::Fd, old_path, new_fd.fd as wasi::Fd, new_path) - .map_err(err2io) + wasi::path_rename( + self.as_raw_fd() as wasi::Fd, + old_path, + new_fd.as_raw_fd() as wasi::Fd, + new_path, + ) + .map_err(err2io) } } pub fn filestat_get(&self) -> io::Result<wasi::Filestat> { - unsafe { wasi::fd_filestat_get(self.fd as wasi::Fd).map_err(err2io) } + unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::Fd).map_err(err2io) } } pub fn filestat_set_times( @@ -175,12 +174,13 @@ impl WasiFd { fstflags: wasi::Fstflags, ) -> io::Result<()> { unsafe { - wasi::fd_filestat_set_times(self.fd as wasi::Fd, atim, mtim, fstflags).map_err(err2io) + wasi::fd_filestat_set_times(self.as_raw_fd() as wasi::Fd, atim, mtim, fstflags) + .map_err(err2io) } } pub fn filestat_set_size(&self, size: u64) -> io::Result<()> { - unsafe { wasi::fd_filestat_set_size(self.fd as wasi::Fd, size).map_err(err2io) } + unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) } } pub fn path_filestat_get( @@ -188,7 +188,9 @@ impl WasiFd { flags: wasi::Lookupflags, path: &str, ) -> io::Result<wasi::Filestat> { - unsafe { wasi::path_filestat_get(self.fd as wasi::Fd, flags, path).map_err(err2io) } + unsafe { + wasi::path_filestat_get(self.as_raw_fd() as wasi::Fd, flags, path).map_err(err2io) + } } pub fn path_filestat_set_times( @@ -200,21 +202,30 @@ impl WasiFd { fstflags: wasi::Fstflags, ) -> io::Result<()> { unsafe { - wasi::path_filestat_set_times(self.fd as wasi::Fd, flags, path, atim, mtim, fstflags) - .map_err(err2io) + wasi::path_filestat_set_times( + self.as_raw_fd() as wasi::Fd, + flags, + path, + atim, + mtim, + fstflags, + ) + .map_err(err2io) } } pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> { - unsafe { wasi::path_symlink(old_path, self.fd as wasi::Fd, new_path).map_err(err2io) } + unsafe { + wasi::path_symlink(old_path, self.as_raw_fd() as wasi::Fd, new_path).map_err(err2io) + } } pub fn unlink_file(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_unlink_file(self.fd as wasi::Fd, path).map_err(err2io) } + unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } pub fn remove_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_remove_directory(self.fd as wasi::Fd, path).map_err(err2io) } + unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } pub fn sock_recv( @@ -222,11 +233,15 @@ impl WasiFd { ri_data: &mut [IoSliceMut<'_>], ri_flags: wasi::Riflags, ) -> io::Result<(usize, wasi::Roflags)> { - unsafe { wasi::sock_recv(self.fd as wasi::Fd, iovec(ri_data), ri_flags).map_err(err2io) } + unsafe { + wasi::sock_recv(self.as_raw_fd() as wasi::Fd, iovec(ri_data), ri_flags).map_err(err2io) + } } pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result<usize> { - unsafe { wasi::sock_send(self.fd as wasi::Fd, ciovec(si_data), si_flags).map_err(err2io) } + unsafe { + wasi::sock_send(self.as_raw_fd() as wasi::Fd, ciovec(si_data), si_flags).map_err(err2io) + } } pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> { @@ -235,14 +250,54 @@ impl WasiFd { Shutdown::Write => wasi::SDFLAGS_WR, Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD, }; - unsafe { wasi::sock_shutdown(self.fd as wasi::Fd, how).map_err(err2io) } + unsafe { wasi::sock_shutdown(self.as_raw_fd() as wasi::Fd, how).map_err(err2io) } + } +} + +impl AsInner<OwnedFd> for WasiFd { + fn as_inner(&self) -> &OwnedFd { + &self.fd + } +} + +impl AsInnerMut<OwnedFd> for WasiFd { + fn as_inner_mut(&mut self) -> &mut OwnedFd { + &mut self.fd + } +} + +impl IntoInner<OwnedFd> for WasiFd { + fn into_inner(self) -> OwnedFd { + self.fd + } +} + +impl FromInner<OwnedFd> for WasiFd { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self { fd: owned_fd } + } +} + +impl AsFd for WasiFd { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +impl AsRawFd for WasiFd { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl IntoRawFd for WasiFd { + fn into_raw_fd(self) -> RawFd { + self.fd.into_raw_fd() } } -impl Drop for WasiFd { - fn drop(&mut self) { - // FIXME: can we handle the return code here even though we can't on - // unix? - let _ = unsafe { wasi::fd_close(self.fd as wasi::Fd) }; +impl FromRawFd for WasiFd { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } } } diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 55c9c652a8b..984dda8dc0b 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -8,12 +8,13 @@ use crate::iter; use crate::mem::{self, ManuallyDrop}; use crate::os::raw::c_int; use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::ptr; use crate::sync::Arc; use crate::sys::time::SystemTime; use crate::sys::unsupported; -use crate::sys_common::FromInner; +use crate::sys_common::{AsInner, FromInner, IntoInner}; pub use crate::sys_common::fs::{remove_dir_all, try_exists}; @@ -442,22 +443,50 @@ impl File { unsupported() } - pub fn fd(&self) -> &WasiFd { + pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> { + read_link(&self.fd, file) + } +} + +impl AsInner<WasiFd> for File { + fn as_inner(&self) -> &WasiFd { &self.fd } +} - pub fn into_fd(self) -> WasiFd { +impl IntoInner<WasiFd> for File { + fn into_inner(self) -> WasiFd { self.fd } +} - pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> { - read_link(&self.fd, file) +impl FromInner<WasiFd> for File { + fn from_inner(fd: WasiFd) -> File { + File { fd } + } +} + +impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +impl AsRawFd for File { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.fd.into_raw_fd() } } -impl FromInner<c_int> for File { - fn from_inner(fd: c_int) -> File { - unsafe { File { fd: WasiFd::from_raw(fd) } } +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } } } @@ -474,7 +503,7 @@ impl DirBuilder { impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("File").field("fd", &self.fd.as_raw()).finish() + f.debug_struct("File").field("fd", &self.as_raw_fd()).finish() } } @@ -654,7 +683,7 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> { let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); return Ok(( - ManuallyDrop::new(WasiFd::from_raw(fd as c_int)), + ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)), PathBuf::from(OsString::from_vec(relative)), )); } diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs index 50b7352933e..c7c4a9f6efd 100644 --- a/library/std/src/sys/wasi/net.rs +++ b/library/std/src/sys/wasi/net.rs @@ -5,13 +5,57 @@ use crate::convert::TryFrom; use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::os::raw::c_int; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::unsupported; -use crate::sys_common::FromInner; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; +pub struct Socket(WasiFd); + pub struct TcpStream { - fd: WasiFd, + inner: Socket, +} + +impl AsInner<WasiFd> for Socket { + fn as_inner(&self) -> &WasiFd { + &self.0 + } +} + +impl IntoInner<WasiFd> for Socket { + fn into_inner(self) -> WasiFd { + self.0 + } +} + +impl FromInner<WasiFd> for Socket { + fn from_inner(inner: WasiFd) -> Socket { + Socket(inner) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } } impl TcpStream { @@ -107,29 +151,29 @@ impl TcpStream { unsupported() } - pub fn fd(&self) -> &WasiFd { - &self.fd + pub fn socket(&self) -> &Socket { + &self.inner } - pub fn into_fd(self) -> WasiFd { - self.fd + pub fn into_socket(self) -> Socket { + self.inner } } -impl FromInner<c_int> for TcpStream { - fn from_inner(fd: c_int) -> TcpStream { - unsafe { TcpStream { fd: WasiFd::from_raw(fd) } } +impl FromInner<Socket> for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } } } impl fmt::Debug for TcpStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TcpStream").field("fd", &self.fd.as_raw()).finish() + f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish() } } pub struct TcpListener { - fd: WasiFd, + inner: Socket, } impl TcpListener { @@ -173,29 +217,41 @@ impl TcpListener { unsupported() } - pub fn fd(&self) -> &WasiFd { - &self.fd + pub fn socket(&self) -> &Socket { + &self.inner } - pub fn into_fd(self) -> WasiFd { - self.fd + pub fn into_socket(self) -> Socket { + self.inner } } -impl FromInner<c_int> for TcpListener { - fn from_inner(fd: c_int) -> TcpListener { - unsafe { TcpListener { fd: WasiFd::from_raw(fd) } } +impl AsInner<Socket> for TcpListener { + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner<Socket> for TcpListener { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner<Socket> for TcpListener { + fn from_inner(inner: Socket) -> TcpListener { + TcpListener { inner } } } impl fmt::Debug for TcpListener { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TcpListener").field("fd", &self.fd.as_raw()).finish() + f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish() } } pub struct UdpSocket { - fd: WasiFd, + inner: Socket, } impl UdpSocket { @@ -323,24 +379,36 @@ impl UdpSocket { unsupported() } - pub fn fd(&self) -> &WasiFd { - &self.fd + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner } +} + +impl AsInner<Socket> for UdpSocket { + fn as_inner(&self) -> &Socket { + &self.inner + } +} - pub fn into_fd(self) -> WasiFd { - self.fd +impl IntoInner<Socket> for UdpSocket { + fn into_inner(self) -> Socket { + self.inner } } -impl FromInner<c_int> for UdpSocket { - fn from_inner(fd: c_int) -> UdpSocket { - unsafe { UdpSocket { fd: WasiFd::from_raw(fd) } } +impl FromInner<Socket> for UdpSocket { + fn from_inner(inner: Socket) -> UdpSocket { + UdpSocket { inner } } } impl fmt::Debug for UdpSocket { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("UdpSocket").field("fd", &self.fd.as_raw()).finish() + f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish() } } diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs index 8782f333a1f..2c8f394cd47 100644 --- a/library/std/src/sys/wasi/stdio.rs +++ b/library/std/src/sys/wasi/stdio.rs @@ -4,6 +4,7 @@ use super::fd::WasiFd; use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem::ManuallyDrop; use crate::os::raw; +use crate::os::wasi::io::{AsRawFd, FromRawFd}; pub struct Stdin; pub struct Stdout; @@ -13,9 +14,11 @@ impl Stdin { pub const fn new() -> Stdin { Stdin } +} +impl AsRawFd for Stdin { #[inline] - pub fn as_raw_fd(&self) -> raw::c_int { + fn as_raw_fd(&self) -> raw::c_int { 0 } } @@ -26,7 +29,7 @@ impl io::Read for Stdin { } fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).read(data) + ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read(data) } #[inline] @@ -39,9 +42,11 @@ impl Stdout { pub const fn new() -> Stdout { Stdout } +} +impl AsRawFd for Stdout { #[inline] - pub fn as_raw_fd(&self) -> raw::c_int { + fn as_raw_fd(&self) -> raw::c_int { 1 } } @@ -52,7 +57,7 @@ impl io::Write for Stdout { } fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) + ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data) } #[inline] @@ -68,9 +73,11 @@ impl Stderr { pub const fn new() -> Stderr { Stderr } +} +impl AsRawFd for Stderr { #[inline] - pub fn as_raw_fd(&self) -> raw::c_int { + fn as_raw_fd(&self) -> raw::c_int { 2 } } @@ -81,7 +88,7 @@ impl io::Write for Stderr { } fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) + ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data) } #[inline] diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index c677adae688..0c1a50e231c 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -4,6 +4,7 @@ use crate::ffi::OsString; use crate::fmt; use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; +use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::path::{Path, PathBuf}; use crate::ptr; use crate::slice; @@ -11,7 +12,7 @@ use crate::sync::Arc; use crate::sys::handle::Handle; use crate::sys::time::SystemTime; use crate::sys::{c, cvt}; -use crate::sys_common::FromInner; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use super::to_u16s; @@ -295,12 +296,12 @@ impl File { if handle == c::INVALID_HANDLE_VALUE { Err(Error::last_os_error()) } else { - Ok(File { handle: Handle::new(handle) }) + unsafe { Ok(File { handle: Handle::from_raw_handle(handle) }) } } } pub fn fsync(&self) -> io::Result<()> { - cvt(unsafe { c::FlushFileBuffers(self.handle.raw()) })?; + cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?; Ok(()) } @@ -313,7 +314,7 @@ impl File { let size = mem::size_of_val(&info); cvt(unsafe { c::SetFileInformationByHandle( - self.handle.raw(), + self.handle.as_raw_handle(), c::FileEndOfFileInfo, &mut info as *mut _ as *mut _, size as c::DWORD, @@ -326,7 +327,7 @@ impl File { pub fn file_attr(&self) -> io::Result<FileAttr> { unsafe { let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); - cvt(c::GetFileInformationByHandle(self.handle.raw(), &mut info))?; + cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; let mut reparse_tag = 0; if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; @@ -449,7 +450,7 @@ impl File { }; let pos = pos as c::LARGE_INTEGER; let mut newpos = 0; - cvt(unsafe { c::SetFilePointerEx(self.handle.raw(), pos, &mut newpos, whence) })?; + cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?; Ok(newpos as u64) } @@ -457,14 +458,6 @@ impl File { Ok(File { handle: self.handle.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)? }) } - pub fn handle(&self) -> &Handle { - &self.handle - } - - pub fn into_handle(self) -> Handle { - self.handle - } - fn reparse_point<'a>( &self, space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE], @@ -473,7 +466,7 @@ impl File { let mut bytes = 0; cvt({ c::DeviceIoControl( - self.handle.raw(), + self.handle.as_raw_handle(), c::FSCTL_GET_REPARSE_POINT, ptr::null_mut(), 0, @@ -541,7 +534,7 @@ impl File { let size = mem::size_of_val(&info); cvt(unsafe { c::SetFileInformationByHandle( - self.handle.raw(), + self.handle.as_raw_handle(), c::FileBasicInfo, &mut info as *mut _ as *mut _, size as c::DWORD, @@ -551,9 +544,45 @@ impl File { } } -impl FromInner<c::HANDLE> for File { - fn from_inner(handle: c::HANDLE) -> File { - File { handle: Handle::new(handle) } +impl AsInner<Handle> for File { + fn as_inner(&self) -> &Handle { + &self.handle + } +} + +impl IntoInner<Handle> for File { + fn into_inner(self) -> Handle { + self.handle + } +} + +impl FromInner<Handle> for File { + fn from_inner(handle: Handle) -> File { + File { handle: handle } + } +} + +impl AsHandle for File { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.as_inner().as_handle() + } +} + +impl AsRawHandle for File { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().as_raw_handle() + } +} + +impl IntoRawHandle for File { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_raw_handle() + } +} + +impl FromRawHandle for File { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) } } } @@ -561,7 +590,7 @@ impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME(#24570): add more info here (e.g., mode) let mut b = f.debug_struct("File"); - b.field("handle", &self.handle.raw()); + b.field("handle", &self.handle.as_raw_handle()); if let Ok(path) = get_path(&self) { b.field("path", &path); } @@ -838,7 +867,7 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { fn get_path(f: &File) -> io::Result<PathBuf> { super::fill_utf16_buf( |buf, sz| unsafe { - c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, c::VOLUME_NAME_DOS) + c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS) }, |buf| PathBuf::from(OsString::from_wide(buf)), ) @@ -909,7 +938,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { opts.write(true); opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); let f = File::open(junction, &opts)?; - let h = f.handle().raw(); + let h = f.as_inner().as_raw_handle(); unsafe { let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs index 0d4baa3b340..21d86b00226 100644 --- a/library/std/src/sys/windows/handle.rs +++ b/library/std/src/sys/windows/handle.rs @@ -3,76 +3,87 @@ use crate::cmp; use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read}; use crate::mem; -use crate::ops::Deref; +use crate::os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, +}; use crate::ptr; use crate::sys::c; use crate::sys::cvt; +use crate::sys_common::{AsInner, FromInner, IntoInner}; /// An owned container for `HANDLE` object, closing them on Drop. /// /// All methods are inherited through a `Deref` impl to `RawHandle` -pub struct Handle(RawHandle); - -/// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference -/// as well as Rust-y methods. -/// -/// This does **not** drop the handle when it goes out of scope, use `Handle` -/// instead for that. -#[derive(Copy, Clone)] -pub struct RawHandle(c::HANDLE); - -unsafe impl Send for RawHandle {} -unsafe impl Sync for RawHandle {} +pub struct Handle(OwnedHandle); impl Handle { - pub fn new(handle: c::HANDLE) -> Handle { - Handle(RawHandle::new(handle)) - } - pub fn new_event(manual: bool, init: bool) -> io::Result<Handle> { unsafe { let event = c::CreateEventW(ptr::null_mut(), manual as c::BOOL, init as c::BOOL, ptr::null()); - if event.is_null() { Err(io::Error::last_os_error()) } else { Ok(Handle::new(event)) } + if event.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(Handle::from_raw_handle(event)) + } } } +} - pub fn into_raw(self) -> c::HANDLE { - let ret = self.raw(); - mem::forget(self); - ret +impl AsInner<OwnedHandle> for Handle { + fn as_inner(&self) -> &OwnedHandle { + &self.0 } } -impl Deref for Handle { - type Target = RawHandle; - fn deref(&self) -> &RawHandle { - &self.0 +impl IntoInner<OwnedHandle> for Handle { + fn into_inner(self) -> OwnedHandle { + self.0 } } -impl Drop for Handle { - fn drop(&mut self) { - unsafe { - let _ = c::CloseHandle(self.raw()); - } +impl FromInner<OwnedHandle> for Handle { + fn from_inner(file_desc: OwnedHandle) -> Self { + Self(file_desc) } } -impl RawHandle { - pub fn new(handle: c::HANDLE) -> RawHandle { - RawHandle(handle) +impl AsHandle for Handle { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() } +} - pub fn raw(&self) -> c::HANDLE { - self.0 +impl AsRawHandle for Handle { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() } +} +impl IntoRawHandle for Handle { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +impl FromRawHandle for Handle { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(FromRawHandle::from_raw_handle(raw_handle)) + } +} + +impl Handle { pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { let mut read = 0; let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD; let res = cvt(unsafe { - c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, len, &mut read, ptr::null_mut()) + c::ReadFile( + self.as_raw_handle(), + buf.as_mut_ptr() as c::LPVOID, + len, + &mut read, + ptr::null_mut(), + ) }); match res { @@ -104,7 +115,13 @@ impl RawHandle { let mut overlapped: c::OVERLAPPED = mem::zeroed(); overlapped.Offset = offset as u32; overlapped.OffsetHigh = (offset >> 32) as u32; - cvt(c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, len, &mut read, &mut overlapped)) + cvt(c::ReadFile( + self.as_raw_handle(), + buf.as_mut_ptr() as c::LPVOID, + len, + &mut read, + &mut overlapped, + )) }; match res { Ok(_) => Ok(read as usize), @@ -120,7 +137,13 @@ impl RawHandle { ) -> io::Result<Option<usize>> { let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD; let mut amt = 0; - let res = cvt(c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped)); + let res = cvt(c::ReadFile( + self.as_raw_handle(), + buf.as_ptr() as c::LPVOID, + len, + &mut amt, + overlapped, + )); match res { Ok(_) => Ok(Some(amt as usize)), Err(e) => { @@ -143,7 +166,8 @@ impl RawHandle { unsafe { let mut bytes = 0; let wait = if wait { c::TRUE } else { c::FALSE }; - let res = cvt(c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait)); + let res = + cvt(c::GetOverlappedResult(self.as_raw_handle(), overlapped, &mut bytes, wait)); match res { Ok(_) => Ok(bytes as usize), Err(e) => { @@ -160,14 +184,20 @@ impl RawHandle { } pub fn cancel_io(&self) -> io::Result<()> { - unsafe { cvt(c::CancelIo(self.raw())).map(drop) } + unsafe { cvt(c::CancelIo(self.as_raw_handle())).map(drop) } } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let mut amt = 0; let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD; cvt(unsafe { - c::WriteFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, ptr::null_mut()) + c::WriteFile( + self.as_raw_handle(), + buf.as_ptr() as c::LPVOID, + len, + &mut amt, + ptr::null_mut(), + ) })?; Ok(amt as usize) } @@ -189,7 +219,7 @@ impl RawHandle { overlapped.Offset = offset as u32; overlapped.OffsetHigh = (offset >> 32) as u32; cvt(c::WriteFile( - self.0, + self.as_raw_handle(), buf.as_ptr() as c::LPVOID, len, &mut written, @@ -210,7 +240,7 @@ impl RawHandle { let cur_proc = c::GetCurrentProcess(); c::DuplicateHandle( cur_proc, - self.0, + self.as_raw_handle(), cur_proc, &mut ret, access, @@ -218,11 +248,11 @@ impl RawHandle { options, ) })?; - Ok(Handle::new(ret)) + unsafe { Ok(Handle::from_raw_handle(ret)) } } } -impl<'a> Read for &'a RawHandle { +impl<'a> Read for &'a Handle { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) } diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 9cea5c5e63a..55aacb38c6f 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -4,6 +4,9 @@ use crate::cmp; use crate::io::{self, IoSlice, IoSliceMut, Read}; use crate::mem; use crate::net::{Shutdown, SocketAddr}; +use crate::os::windows::io::{ + AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, +}; use crate::ptr; use crate::sync::Once; use crate::sys; @@ -24,7 +27,7 @@ pub mod netc { pub use crate::sys::c::*; } -pub struct Socket(c::SOCKET); +pub struct Socket(OwnedSocket); static INIT: Once = Once::new(); @@ -109,7 +112,7 @@ impl Socket { }; if socket != c::INVALID_SOCKET { - Ok(Self(socket)) + unsafe { Ok(Self::from_raw_socket(socket)) } } else { let error = unsafe { c::WSAGetLastError() }; @@ -124,9 +127,11 @@ impl Socket { return Err(last_error()); } - let socket = Self(socket); - socket.set_no_inherit()?; - Ok(socket) + unsafe { + let socket = Self::from_raw_socket(socket); + socket.set_no_inherit()?; + Ok(socket) + } } } @@ -134,7 +139,7 @@ impl Socket { self.set_nonblocking(true)?; let result = { let (addrp, len) = addr.into_inner(); - let result = unsafe { c::connect(self.0, addrp, len) }; + let result = unsafe { c::connect(self.as_raw_socket(), addrp, len) }; cvt(result).map(drop) }; self.set_nonblocking(false)?; @@ -160,7 +165,7 @@ impl Socket { let fds = { let mut fds = unsafe { mem::zeroed::<c::fd_set>() }; fds.fd_count = 1; - fds.fd_array[0] = self.0; + fds.fd_array[0] = self.as_raw_socket(); fds }; @@ -194,17 +199,19 @@ impl Socket { } pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result<Socket> { - let socket = unsafe { c::accept(self.0, storage, len) }; + let socket = unsafe { c::accept(self.as_raw_socket(), storage, len) }; match socket { c::INVALID_SOCKET => Err(last_error()), - _ => Ok(Self(socket)), + _ => unsafe { Ok(Self::from_raw_socket(socket)) }, } } pub fn duplicate(&self) -> io::Result<Socket> { let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFO>() }; - let result = unsafe { c::WSADuplicateSocketW(self.0, c::GetCurrentProcessId(), &mut info) }; + let result = unsafe { + c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info) + }; cvt(result)?; let socket = unsafe { c::WSASocketW( @@ -218,7 +225,7 @@ impl Socket { }; if socket != c::INVALID_SOCKET { - Ok(Self(socket)) + unsafe { Ok(Self::from_inner(OwnedSocket::from_raw_socket(socket))) } } else { let error = unsafe { c::WSAGetLastError() }; @@ -241,9 +248,11 @@ impl Socket { return Err(last_error()); } - let socket = Self(socket); - socket.set_no_inherit()?; - Ok(socket) + unsafe { + let socket = Self::from_inner(OwnedSocket::from_raw_socket(socket)); + socket.set_no_inherit()?; + Ok(socket) + } } } @@ -251,7 +260,8 @@ impl Socket { // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. let length = cmp::min(buf.len(), i32::MAX as usize) as i32; - let result = unsafe { c::recv(self.0, buf.as_mut_ptr() as *mut _, length, flags) }; + let result = + unsafe { c::recv(self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags) }; match result { c::SOCKET_ERROR => { @@ -279,7 +289,7 @@ impl Socket { let mut flags = 0; let result = unsafe { c::WSARecv( - self.0, + self.as_raw_socket(), bufs.as_mut_ptr() as *mut c::WSABUF, length, &mut nread, @@ -325,7 +335,7 @@ impl Socket { // do the same on windows to map a shut down socket to returning EOF. let result = unsafe { c::recvfrom( - self.0, + self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags, @@ -361,7 +371,7 @@ impl Socket { let mut nwritten = 0; let result = unsafe { c::WSASend( - self.0, + self.as_raw_socket(), bufs.as_ptr() as *const c::WSABUF as *mut _, length, &mut nwritten, @@ -408,8 +418,10 @@ impl Socket { #[cfg(not(target_vendor = "uwp"))] fn set_no_inherit(&self) -> io::Result<()> { - sys::cvt(unsafe { c::SetHandleInformation(self.0 as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0) }) - .map(drop) + sys::cvt(unsafe { + c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0) + }) + .map(drop) } #[cfg(target_vendor = "uwp")] @@ -423,13 +435,14 @@ impl Socket { Shutdown::Read => c::SD_RECEIVE, Shutdown::Both => c::SD_BOTH, }; - let result = unsafe { c::shutdown(self.0, how) }; + let result = unsafe { c::shutdown(self.as_raw_socket(), how) }; cvt(result).map(drop) } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as c_ulong; - let result = unsafe { c::ioctlsocket(self.0, c::FIONBIO as c_int, &mut nonblocking) }; + let result = + unsafe { c::ioctlsocket(self.as_raw_socket(), c::FIONBIO as c_int, &mut nonblocking) }; cvt(result).map(drop) } @@ -446,6 +459,11 @@ impl Socket { let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawSocket { + self.as_inner().as_raw_socket() + } } #[unstable(reason = "not public", issue = "none", feature = "fd_read")] @@ -455,28 +473,44 @@ impl<'a> Read for &'a Socket { } } -impl Drop for Socket { - fn drop(&mut self) { - let _ = unsafe { c::closesocket(self.0) }; +impl AsInner<OwnedSocket> for Socket { + fn as_inner(&self) -> &OwnedSocket { + &self.0 } } -impl AsInner<c::SOCKET> for Socket { - fn as_inner(&self) -> &c::SOCKET { - &self.0 +impl FromInner<OwnedSocket> for Socket { + fn from_inner(sock: OwnedSocket) -> Socket { + Socket(sock) } } -impl FromInner<c::SOCKET> for Socket { - fn from_inner(sock: c::SOCKET) -> Socket { - Socket(sock) +impl IntoInner<OwnedSocket> for Socket { + fn into_inner(self) -> OwnedSocket { + self.0 + } +} + +impl AsSocket for Socket { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } +} + +impl AsRawSocket for Socket { + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl IntoRawSocket for Socket { + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() } } -impl IntoInner<c::SOCKET> for Socket { - fn into_inner(self) -> c::SOCKET { - let ret = self.0; - mem::forget(self); - ret +impl FromRawSocket for Socket { + unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self { + Self(FromRawSocket::from_raw_socket(raw_socket)) } } diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs index 8db97ba50a8..883690c4831 100644 --- a/library/std/src/sys/windows/os.rs +++ b/library/std/src/sys/windows/os.rs @@ -288,7 +288,7 @@ fn home_dir_crt() -> Option<PathBuf> { if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 { return None; } - let _handle = Handle::new(token); + let _handle = Handle::from_raw_handle(token); super::fill_utf16_buf( |buf, mut sz| { match c::GetUserProfileDirectoryW(token, buf, &mut sz) { diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs index 104a8db4659..63d3d6c5ed4 100644 --- a/library/std/src/sys/windows/pipe.rs +++ b/library/std/src/sys/windows/pipe.rs @@ -12,6 +12,7 @@ use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::hashmap_random_keys; +use crate::sys_common::IntoInner; //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes @@ -21,6 +22,12 @@ pub struct AnonPipe { inner: Handle, } +impl IntoInner<Handle> for AnonPipe { + fn into_inner(self) -> Handle { + self.inner + } +} + pub struct Pipes { pub ours: AnonPipe, pub theirs: AnonPipe, @@ -123,7 +130,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res } return Err(err); } - ours = Handle::new(handle); + ours = Handle::from_raw_handle(handle); break; } @@ -146,11 +153,11 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res }; opts.security_attributes(&mut sa); let theirs = File::open(Path::new(&name), &opts)?; - let theirs = AnonPipe { inner: theirs.into_handle() }; + let theirs = AnonPipe { inner: theirs.into_inner() }; Ok(Pipes { ours: AnonPipe { inner: ours }, - theirs: AnonPipe { inner: theirs.into_handle() }, + theirs: AnonPipe { inner: theirs.into_inner() }, }) } } @@ -207,7 +214,7 @@ pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> let mut p1 = AsyncPipe::new(p1, v1)?; let mut p2 = AsyncPipe::new(p2, v2)?; - let objs = [p1.event.raw(), p2.event.raw()]; + let objs = [p1.event.as_raw_handle(), p2.event.as_raw_handle()]; // In a loop we wait for either pipe's scheduled read operation to complete. // If the operation completes with 0 bytes, that means EOF was reached, in @@ -262,7 +269,7 @@ impl<'a> AsyncPipe<'a> { // I/O operation is successfully scheduled (what we want). let event = Handle::new_event(true, true)?; let mut overlapped: Box<c::OVERLAPPED> = unsafe { Box::new(mem::zeroed()) }; - overlapped.hEvent = event.raw(); + overlapped.hEvent = event.as_raw_handle(); Ok(AsyncPipe { pipe, overlapped, event, dst, state: State::NotReading }) } diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index ae193b82e91..ccff90629a3 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -15,6 +15,7 @@ use crate::io::{self, Error, ErrorKind}; use crate::mem; use crate::num::NonZeroI32; use crate::os::windows::ffi::OsStrExt; +use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle}; use crate::path::Path; use crate::ptr; use crate::sys::c; @@ -26,7 +27,7 @@ use crate::sys::pipe::{self, AnonPipe}; use crate::sys::stdio; use crate::sys_common::mutex::StaticMutex; use crate::sys_common::process::{CommandEnv, CommandEnvs}; -use crate::sys_common::AsInner; +use crate::sys_common::{AsInner, IntoInner}; use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS}; @@ -316,9 +317,9 @@ impl Command { let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?; let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?; let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?; - si.hStdInput = stdin.raw(); - si.hStdOutput = stdout.raw(); - si.hStdError = stderr.raw(); + si.hStdInput = stdin.as_raw_handle(); + si.hStdOutput = stdout.as_raw_handle(); + si.hStdError = stderr.as_raw_handle(); unsafe { cvt(c::CreateProcessW( @@ -338,9 +339,11 @@ impl Command { // We close the thread handle because we don't care about keeping // the thread id valid, and we aren't keeping the thread handle // around to be able to close it later. - drop(Handle::new(pi.hThread)); + unsafe { + drop(Handle::from_raw_handle(pi.hThread)); - Ok((Process { handle: Handle::new(pi.hProcess) }, pipes)) + Ok((Process { handle: Handle::from_raw_handle(pi.hProcess) }, pipes)) + } } } @@ -365,13 +368,13 @@ impl Stdio { // should still be unavailable so propagate the // INVALID_HANDLE_VALUE. Stdio::Inherit => match stdio::get_handle(stdio_id) { - Ok(io) => { - let io = Handle::new(io); + Ok(io) => unsafe { + let io = Handle::from_raw_handle(io); let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); - io.into_raw(); + io.into_raw_handle(); ret - } - Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)), + }, + Err(..) => unsafe { Ok(Handle::from_raw_handle(c::INVALID_HANDLE_VALUE)) }, }, Stdio::MakePipe => { @@ -397,7 +400,7 @@ impl Stdio { opts.read(stdio_id == c::STD_INPUT_HANDLE); opts.write(stdio_id != c::STD_INPUT_HANDLE); opts.security_attributes(&mut sa); - File::open(Path::new("NUL"), &opts).map(|file| file.into_handle()) + File::open(Path::new("NUL"), &opts).map(|file| file.into_inner()) } } } @@ -411,7 +414,7 @@ impl From<AnonPipe> for Stdio { impl From<File> for Stdio { fn from(file: File) -> Stdio { - Stdio::Handle(file.into_handle()) + Stdio::Handle(file.into_inner()) } } @@ -430,29 +433,29 @@ pub struct Process { impl Process { pub fn kill(&mut self) -> io::Result<()> { - cvt(unsafe { c::TerminateProcess(self.handle.raw(), 1) })?; + cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?; Ok(()) } pub fn id(&self) -> u32 { - unsafe { c::GetProcessId(self.handle.raw()) as u32 } + unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 } } pub fn wait(&mut self) -> io::Result<ExitStatus> { unsafe { - let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE); + let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE); if res != c::WAIT_OBJECT_0 { return Err(Error::last_os_error()); } let mut status = 0; - cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; + cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?; Ok(ExitStatus(status)) } } pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { unsafe { - match c::WaitForSingleObject(self.handle.raw(), 0) { + match c::WaitForSingleObject(self.handle.as_raw_handle(), 0) { c::WAIT_OBJECT_0 => {} c::WAIT_TIMEOUT => { return Ok(None); @@ -460,7 +463,7 @@ impl Process { _ => return Err(io::Error::last_os_error()), } let mut status = 0; - cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; + cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?; Ok(Some(ExitStatus(status))) } } diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index 2973951fe90..1cf0e9f0cf1 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -3,6 +3,7 @@ use crate::char::decode_utf16; use crate::cmp; use crate::io; +use crate::os::windows::io::{FromRawHandle, IntoRawHandle}; use crate::ptr; use crate::str; use crate::sys::c; @@ -53,10 +54,12 @@ fn is_console(handle: c::HANDLE) -> bool { fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result<usize> { let handle = get_handle(handle_id)?; if !is_console(handle) { - let handle = Handle::new(handle); - let ret = handle.write(data); - handle.into_raw(); // Don't close the handle - return ret; + unsafe { + let handle = Handle::from_raw_handle(handle); + let ret = handle.write(data); + handle.into_raw_handle(); // Don't close the handle + return ret; + } } // As the console is meant for presenting text, we assume bytes of `data` come from a string @@ -140,10 +143,12 @@ impl io::Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { let handle = get_handle(c::STD_INPUT_HANDLE)?; if !is_console(handle) { - let handle = Handle::new(handle); - let ret = handle.read(buf); - handle.into_raw(); // Don't close the handle - return ret; + unsafe { + let handle = Handle::from_raw_handle(handle); + let ret = handle.read(buf); + handle.into_raw_handle(); // Don't close the handle + return ret; + } } if buf.len() == 0 { diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs index ef7a9733fd8..a5293133b3a 100644 --- a/library/std/src/sys/windows/thread.rs +++ b/library/std/src/sys/windows/thread.rs @@ -1,6 +1,7 @@ use crate::ffi::CStr; use crate::io; use crate::num::NonZeroUsize; +use crate::os::windows::io::{AsRawHandle, FromRawHandle}; use crate::ptr; use crate::sys::c; use crate::sys::handle::Handle; @@ -45,7 +46,7 @@ impl Thread { drop(Box::from_raw(p)); Err(io::Error::last_os_error()) } else { - Ok(Thread { handle: Handle::new(ret) }) + Ok(Thread { handle: Handle::from_raw_handle(ret) }) }; extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { @@ -71,7 +72,7 @@ impl Thread { } pub fn join(self) { - let rc = unsafe { c::WaitForSingleObject(self.handle.raw(), c::INFINITE) }; + let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; if rc == c::WAIT_FAILED { panic!("failed to join on thread: {}", io::Error::last_os_error()); } diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index d5f29c4a439..0ffa5c01dd3 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -61,13 +61,7 @@ cfg_if::cfg_if! { pub fn setsockopt<T>(sock: &Socket, opt: c_int, val: c_int, payload: T) -> io::Result<()> { unsafe { let payload = &payload as *const T as *const c_void; - cvt(c::setsockopt( - *sock.as_inner(), - opt, - val, - payload, - mem::size_of::<T>() as c::socklen_t, - ))?; + cvt(c::setsockopt(sock.as_raw(), opt, val, payload, mem::size_of::<T>() as c::socklen_t))?; Ok(()) } } @@ -76,7 +70,7 @@ pub fn getsockopt<T: Copy>(sock: &Socket, opt: c_int, val: c_int) -> io::Result< unsafe { let mut slot: T = mem::zeroed(); let mut len = mem::size_of::<T>() as c::socklen_t; - cvt(c::getsockopt(*sock.as_inner(), opt, val, &mut slot as *mut _ as *mut _, &mut len))?; + cvt(c::getsockopt(sock.as_raw(), opt, val, &mut slot as *mut _ as *mut _, &mut len))?; assert_eq!(len as usize, mem::size_of::<T>()); Ok(slot) } @@ -217,7 +211,7 @@ impl TcpStream { let sock = Socket::new(addr, c::SOCK_STREAM)?; let (addrp, len) = addr.into_inner(); - cvt_r(|| unsafe { c::connect(*sock.as_inner(), addrp, len) })?; + cvt_r(|| unsafe { c::connect(sock.as_raw(), addrp, len) })?; Ok(TcpStream { inner: sock }) } @@ -273,7 +267,7 @@ impl TcpStream { pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t; let ret = cvt(unsafe { - c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) })?; Ok(ret as usize) } @@ -288,11 +282,11 @@ impl TcpStream { } pub fn peer_addr(&self) -> io::Result<SocketAddr> { - sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) } pub fn socket_addr(&self) -> io::Result<SocketAddr> { - sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) } pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { @@ -348,7 +342,7 @@ impl fmt::Debug for TcpStream { } let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_inner()).finish() + res.field(name, &self.inner.as_raw()).finish() } } @@ -380,10 +374,10 @@ impl TcpListener { // Bind our new socket let (addrp, len) = addr.into_inner(); - cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?; + cvt(unsafe { c::bind(sock.as_raw(), addrp, len as _) })?; // Start listening - cvt(unsafe { c::listen(*sock.as_inner(), 128) })?; + cvt(unsafe { c::listen(sock.as_raw(), 128) })?; Ok(TcpListener { inner: sock }) } @@ -396,7 +390,7 @@ impl TcpListener { } pub fn socket_addr(&self) -> io::Result<SocketAddr> { - sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { @@ -453,7 +447,7 @@ impl fmt::Debug for TcpListener { } let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_inner()).finish() + res.field(name, &self.inner.as_raw()).finish() } } @@ -473,7 +467,7 @@ impl UdpSocket { let sock = Socket::new(addr, c::SOCK_DGRAM)?; let (addrp, len) = addr.into_inner(); - cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?; + cvt(unsafe { c::bind(sock.as_raw(), addrp, len as _) })?; Ok(UdpSocket { inner: sock }) } @@ -486,11 +480,11 @@ impl UdpSocket { } pub fn peer_addr(&self) -> io::Result<SocketAddr> { - sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) } pub fn socket_addr(&self) -> io::Result<SocketAddr> { - sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) } pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { @@ -506,7 +500,7 @@ impl UdpSocket { let (dstp, dstlen) = dst.into_inner(); let ret = cvt(unsafe { c::sendto( - *self.inner.as_inner(), + self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL, @@ -643,14 +637,14 @@ impl UdpSocket { pub fn send(&self, buf: &[u8]) -> io::Result<usize> { let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t; let ret = cvt(unsafe { - c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) })?; Ok(ret as usize) } pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { let (addrp, len) = addr?.into_inner(); - cvt_r(|| unsafe { c::connect(*self.inner.as_inner(), addrp, len) }).map(drop) + cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addrp, len) }).map(drop) } } @@ -669,6 +663,6 @@ impl fmt::Debug for UdpSocket { } let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_inner()).finish() + res.field(name, &self.inner.as_raw()).finish() } } diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 6d70c7270d3..ec105f231e5 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -12,15 +12,14 @@ #![stable(feature = "time", since = "1.3.0")] +mod monotonic; #[cfg(test)] mod tests; -use crate::cmp; use crate::error::Error; use crate::fmt; use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::sys::time; -use crate::sys_common::mutex::StaticMutex; use crate::sys_common::FromInner; #[stable(feature = "time", since = "1.3.0")] @@ -249,14 +248,7 @@ impl Instant { return Instant(os_now); } - static LOCK: StaticMutex = StaticMutex::new(); - static mut LAST_NOW: time::Instant = time::Instant::zero(); - unsafe { - let _lock = LOCK.lock(); - let now = cmp::max(LAST_NOW, os_now); - LAST_NOW = now; - Instant(now) - } + Instant(monotonic::monotonize(os_now)) } /// Returns the amount of time elapsed from another instant to this one. diff --git a/library/std/src/time/monotonic.rs b/library/std/src/time/monotonic.rs new file mode 100644 index 00000000000..27fee6acff3 --- /dev/null +++ b/library/std/src/time/monotonic.rs @@ -0,0 +1,115 @@ +use crate::sys::time; + +#[inline] +pub(super) fn monotonize(raw: time::Instant) -> time::Instant { + inner::monotonize(raw) +} + +#[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))] +pub mod inner { + use crate::sync::atomic::AtomicU64; + use crate::sync::atomic::Ordering::*; + use crate::sys::time; + use crate::time::Duration; + + pub(in crate::time) const ZERO: time::Instant = time::Instant::zero(); + + // bits 30 and 31 are never used since the nanoseconds part never exceeds 10^9 + const UNINITIALIZED: u64 = 0b11 << 30; + static MONO: AtomicU64 = AtomicU64::new(UNINITIALIZED); + + #[inline] + pub(super) fn monotonize(raw: time::Instant) -> time::Instant { + monotonize_impl(&MONO, raw) + } + + #[inline] + pub(in crate::time) fn monotonize_impl(mono: &AtomicU64, raw: time::Instant) -> time::Instant { + let delta = raw.checked_sub_instant(&ZERO).unwrap(); + let secs = delta.as_secs(); + // occupies no more than 30 bits (10^9 seconds) + let nanos = delta.subsec_nanos() as u64; + + // This wraps around every 136 years (2^32 seconds). + // To detect backsliding we use wrapping arithmetic and declare forward steps smaller + // than 2^31 seconds as expected and everything else as a backslide which will be + // monotonized. + // This could be a problem for programs that call instants at intervals greater + // than 68 years. Interstellar probes may want to ensure that actually_monotonic() is true. + let packed = (secs << 32) | nanos; + let old = mono.load(Relaxed); + + if old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2 { + mono.store(packed, Relaxed); + raw + } else { + // Backslide occurred. We reconstruct monotonized time from the upper 32 bit of the + // passed in value and the 64bits loaded from the atomic + let seconds_lower = old >> 32; + let mut seconds_upper = secs & 0xffff_ffff_0000_0000; + if secs & 0xffff_ffff > seconds_lower { + // Backslide caused the lower 32bit of the seconds part to wrap. + // This must be the case because the seconds part is larger even though + // we are in the backslide branch, i.e. the seconds count should be smaller or equal. + // + // We assume that backslides are smaller than 2^32 seconds + // which means we need to add 1 to the upper half to restore it. + // + // Example: + // most recent observed time: 0xA1_0000_0000_0000_0000u128 + // bits stored in AtomicU64: 0x0000_0000_0000_0000u64 + // backslide by 1s + // caller time is 0xA0_ffff_ffff_0000_0000u128 + // -> we can fix up the upper half time by adding 1 << 32 + seconds_upper = seconds_upper.wrapping_add(0x1_0000_0000); + } + let secs = seconds_upper | seconds_lower; + let nanos = old as u32; + ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap() + } + } +} + +#[cfg(target_has_atomic = "128")] +pub mod inner { + use crate::sync::atomic::AtomicU128; + use crate::sync::atomic::Ordering::*; + use crate::sys::time; + use crate::time::Duration; + + const ZERO: time::Instant = time::Instant::zero(); + static MONO: AtomicU128 = AtomicU128::new(0); + + #[inline] + pub(super) fn monotonize(raw: time::Instant) -> time::Instant { + let delta = raw.checked_sub_instant(&ZERO).unwrap(); + // Split into seconds and nanos since Duration doesn't have a + // constructor that takes an u128 + let secs = delta.as_secs() as u128; + let nanos = delta.subsec_nanos() as u128; + let timestamp: u128 = secs << 64 | nanos; + let timestamp = MONO.fetch_max(timestamp, Relaxed).max(timestamp); + let secs = (timestamp >> 64) as u64; + let nanos = timestamp as u32; + ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap() + } +} + +#[cfg(not(any(target_has_atomic = "64", target_has_atomic = "128")))] +pub mod inner { + use crate::cmp; + use crate::sys::time; + use crate::sys_common::mutex::StaticMutex; + + #[inline] + pub(super) fn monotonize(os_now: time::Instant) -> time::Instant { + static LOCK: StaticMutex = StaticMutex::new(); + static mut LAST_NOW: time::Instant = time::Instant::zero(); + unsafe { + let _lock = LOCK.lock(); + let now = cmp::max(LAST_NOW, os_now); + LAST_NOW = now; + now + } + } +} diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs index 20c813fdc70..dc44c9346b6 100644 --- a/library/std/src/time/tests.rs +++ b/library/std/src/time/tests.rs @@ -1,4 +1,6 @@ use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; +#[cfg(not(target_arch = "wasm32"))] +use test::{black_box, Bencher}; macro_rules! assert_almost_eq { ($a:expr, $b:expr) => {{ @@ -13,8 +15,34 @@ macro_rules! assert_almost_eq { #[test] fn instant_monotonic() { let a = Instant::now(); - let b = Instant::now(); - assert!(b >= a); + loop { + let b = Instant::now(); + assert!(b >= a); + if b > a { + break; + } + } +} + +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn instant_monotonic_concurrent() -> crate::thread::Result<()> { + let threads: Vec<_> = (0..8) + .map(|_| { + crate::thread::spawn(|| { + let mut old = Instant::now(); + for _ in 0..5_000_000 { + let new = Instant::now(); + assert!(new >= old); + old = new; + } + }) + }) + .collect(); + for t in threads { + t.join()?; + } + Ok(()) } #[test] @@ -163,3 +191,71 @@ fn since_epoch() { let hundred_twenty_years = thirty_years * 4; assert!(a < hundred_twenty_years); } + +#[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))] +#[test] +fn monotonizer_wrapping_backslide() { + use super::monotonic::inner::{monotonize_impl, ZERO}; + use core::sync::atomic::AtomicU64; + + let reference = AtomicU64::new(0); + + let time = match ZERO.checked_add_duration(&Duration::from_secs(0xffff_ffff)) { + Some(time) => time, + None => { + // platform cannot represent u32::MAX seconds so it won't have to deal with this kind + // of overflow either + return; + } + }; + + let monotonized = monotonize_impl(&reference, time); + let expected = ZERO.checked_add_duration(&Duration::from_secs(1 << 32)).unwrap(); + assert_eq!( + monotonized, expected, + "64bit monotonizer should handle overflows in the seconds part" + ); +} + +macro_rules! bench_instant_threaded { + ($bench_name:ident, $thread_count:expr) => { + #[bench] + #[cfg(not(target_arch = "wasm32"))] + fn $bench_name(b: &mut Bencher) -> crate::thread::Result<()> { + use crate::sync::atomic::{AtomicBool, Ordering}; + use crate::sync::Arc; + + let running = Arc::new(AtomicBool::new(true)); + + let threads: Vec<_> = (0..$thread_count) + .map(|_| { + let flag = Arc::clone(&running); + crate::thread::spawn(move || { + while flag.load(Ordering::Relaxed) { + black_box(Instant::now()); + } + }) + }) + .collect(); + + b.iter(|| { + let a = Instant::now(); + let b = Instant::now(); + assert!(b >= a); + }); + + running.store(false, Ordering::Relaxed); + + for t in threads { + t.join()?; + } + Ok(()) + } + }; +} + +bench_instant_threaded!(instant_contention_01_threads, 0); +bench_instant_threaded!(instant_contention_02_threads, 1); +bench_instant_threaded!(instant_contention_04_threads, 3); +bench_instant_threaded!(instant_contention_08_threads, 7); +bench_instant_threaded!(instant_contention_16_threads, 15); diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index e2f34826111..5911309a044 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1288,7 +1288,7 @@ impl<'a> Builder<'a> { // requirement, but the `-L` library path is not propagated across // separate Cargo projects. We can add LLVM's library path to the // platform-specific environment variable as a workaround. - if mode == Mode::ToolRustc { + if mode == Mode::ToolRustc || mode == Mode::Codegen { if let Some(llvm_config) = self.llvm_config(target) { let llvm_libdir = output(Command::new(&llvm_config).arg("--libdir")); add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo); diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 78c9a252622..d2598995478 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -806,6 +806,10 @@ impl Step for CodegenBackend { let tmp_stamp = out_dir.join(".tmp.stamp"); + builder.info(&format!( + "Building stage{} codegen backend {} ({} -> {})", + compiler.stage, backend, &compiler.host, target + )); let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false); if builder.config.dry_run { return; diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index b32629af4d3..d1397394be7 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -180,6 +180,7 @@ impl Step for Llvm { .define("LLVM_INCLUDE_EXAMPLES", "OFF") .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") + .define("LLVM_INCLUDE_TESTS", "OFF") .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") .define("LLVM_ENABLE_BINDINGS", "OFF") diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index a28762ac485..74e50c60610 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -166,11 +166,6 @@ pub fn check(build: &mut Build) { } for target in &build.targets { - // Can't compile for iOS unless we're on macOS - if target.contains("apple-ios") && !build.build.contains("apple-darwin") { - panic!("the iOS target is only supported on macOS"); - } - build .config .target_config diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 59a1d06dabd..6417f5a984a 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -206,6 +206,10 @@ x--expand-yaml-anchors--remove: run: src/ci/scripts/verify-line-endings.sh <<: *step + - name: ensure backported commits are in upstream branches + run: src/ci/scripts/verify-backported-commits.sh + <<: *step + - name: run the build run: src/ci/scripts/run-build-from-ci.sh env: diff --git a/src/ci/scripts/verify-backported-commits.sh b/src/ci/scripts/verify-backported-commits.sh new file mode 100755 index 00000000000..1023e4b0e28 --- /dev/null +++ b/src/ci/scripts/verify-backported-commits.sh @@ -0,0 +1,150 @@ +#!/bin/bash +# Ensure commits in beta are in master & commits in stable are in beta + master. +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +# We don't care about commits that predate this automation check, so we pass a +# `<limit>` argument to `git cherry`. +BETA_LIMIT="53fd98ca776cb875bc9e5514f56b52eb74f9e7a9" +STABLE_LIMIT="a178d0322ce20e33eac124758e837cbd80a6f633" + +verify_backported_commits_main() { + ci_base_branch=$(ciBaseBranch) + + if [[ "$ci_base_branch" != "beta" && "$ci_base_branch" != "stable" ]]; then + echo 'Skipping. This is only run when merging to the beta or stable branches.' + exit 0 + fi + + echo 'git: unshallowing the repository so we can check commits' + git fetch \ + --no-tags \ + --no-recurse-submodules \ + --progress \ + --prune \ + --unshallow + + if [[ $ci_base_branch == "beta" ]]; then + verify_cherries master "$BETA_LIMIT" \ + || exit 1 + + elif [[ $ci_base_branch == "stable" ]]; then + (verify_cherries master "$STABLE_LIMIT" \ + & verify_cherries beta "$STABLE_LIMIT") \ + || exit 1 + + fi +} + +# Verify all commits in `HEAD` are backports of a commit in <upstream>. See +# https://git-scm.com/docs/git-cherry for an explanation of the arguments. +# +# $1 = <upstream> +# $2 = <limit> +verify_cherries() { + # commits that lack a `backport-of` comment. + local no_backports=() + # commits with an incorrect `backport-of` comment. + local bad_backports=() + + commits=$(git cherry "origin/$1" HEAD "$2") + + if [[ -z "$commits" ]]; then + echo "All commits in \`HEAD\` are present in \`$1\`" + return 0 + fi + + commits=$(echo "$commits" | grep '^\+' | cut -c 3-) + + while read sha; do + # Check each commit in <current>..<upstream> + backport_sha=$(get_backport "$sha") + + if [[ "$backport_sha" == "nothing" ]]; then + echo "✓ \`$sha\` backports nothing" + continue + fi + + if [[ -z "$backport_sha" ]]; then + no_backports+=("$sha") + continue + fi + + if ! is_in_master "$backport_sha"; then + bad_backports+=("$sha") + continue + fi + + echo "✓ \`$sha\` backports \`$backport_sha\`" + done <<< "$commits" + + failure=0 + + if [ ${#no_backports[@]} -ne 0 ]; then + echo 'Error: Could not find backports for all commits.' + echo + echo 'All commits in \`HEAD\` are required to have a corresponding upstream commit.' + echo 'It looks like the following commits:' + echo + for commit in "${no_backports[@]}"; do + echo " $commit" + done + echo + echo "do not match any commits in \`$1\`. If this was intended, add the text" + echo '\`backport-of: <SHA of a commit already in master>\`' + echo 'somewhere in the message of each of these commits.' + echo + failure=1 + fi + + if [ ${#bad_backports[@]} -ne 0 ]; then + echo 'Error: Found incorrectly marked commits.' + echo + echo 'The following commits:' + echo + for commit in "${bad_backports[@]}"; do + echo " $commit" + done + echo + echo 'have commit messages marked \`backport-of: <SHA>\`, but the SHA is not in' + echo '\`master\`.' + echo + failure=1 + fi + + return $failure +} + +# Get the backport of a commit. It echoes one of: +# +# 1. A SHA of the backported commit +# 2. The string "nothing" +# 3. An empty string +# +# $1 = <sha> +get_backport() { + # This regex is: + # + # ^.* - throw away any extra starting characters + # backport-of: - prefix + # \s\? - optional space + # \(\) - capture group + # [a-f0-9]\+\|nothing - a SHA or the text 'nothing' + # .* - throw away any extra ending characters + # \1 - replace it with the first match + # {s//\1/p;q} - print the first occurrence and quit + # + git show -s --format=%B "$1" \ + | sed -n '/^.*backport-of:\s\?\([a-f0-9]\+\|nothing\).*/{s//\1/p;q}' +} + +# Check if a commit is in master. +# +# $1 = <sha> +is_in_master() { + git merge-base --is-ancestor "$1" origin/master 2> /dev/null +} + +verify_backported_commits_main diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 73b5ef0857d..b6ff3890c58 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -332,6 +332,7 @@ impl<'a> Clean<Option<WherePredicate>> for ty::Predicate<'a> { ty::PredicateKind::ConstEvaluatable(..) => None, ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 782ff8df17b..70255749143 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -168,6 +168,7 @@ impl ExternalCrate { crate fn location( &self, extern_url: Option<&str>, + extern_url_takes_precedence: bool, dst: &std::path::Path, tcx: TyCtxt<'_>, ) -> ExternalLocation { @@ -189,8 +190,10 @@ impl ExternalCrate { return Local; } - if let Some(url) = extern_url { - return to_remote(url); + if extern_url_takes_precedence { + if let Some(url) = extern_url { + return to_remote(url); + } } // Failing that, see if there's an attribute specifying where to find this @@ -202,6 +205,7 @@ impl ExternalCrate { .filter_map(|a| a.value_str()) .map(to_remote) .next() + .or(extern_url.map(to_remote)) // NOTE: only matters if `extern_url_takes_precedence` is false .unwrap_or(Unknown) // Well, at least we tried. } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index e44158bc042..eef6985ea30 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -233,6 +233,8 @@ crate struct RenderOptions { crate extension_css: Option<PathBuf>, /// A map of crate names to the URL to use instead of querying the crate's `html_root_url`. crate extern_html_root_urls: BTreeMap<String, String>, + /// Whether to give precedence to `html_root_url` or `--exten-html-root-url`. + crate extern_html_root_takes_precedence: bool, /// A map of the default settings (values are as for DOM storage API). Keys should lack the /// `rustdoc-` prefix. crate default_settings: FxHashMap<String, String>, @@ -658,6 +660,8 @@ impl Options { let show_type_layout = matches.opt_present("show-type-layout"); let nocapture = matches.opt_present("nocapture"); let generate_link_to_definition = matches.opt_present("generate-link-to-definition"); + let extern_html_root_takes_precedence = + matches.opt_present("extern-html-root-takes-precedence"); if generate_link_to_definition && (show_coverage || output_format != OutputFormat::Html) { diag.struct_err( @@ -714,6 +718,7 @@ impl Options { themes, extension_css, extern_html_root_urls, + extern_html_root_takes_precedence, default_settings, resource_suffix, enable_minification, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 512c4ed2d3c..e96eba2f17d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -528,9 +528,7 @@ crate fn run_global_ctxt( let render_options = ctxt.render_options; let mut cache = ctxt.cache; - krate = tcx.sess.time("create_format_cache", || { - cache.populate(krate, tcx, &render_options.extern_html_root_urls, &render_options.output) - }); + krate = tcx.sess.time("create_format_cache", || cache.populate(krate, tcx, &render_options)); // The main crate doc comments are always collapsed. krate.collapsed = true; diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 3d267ca5033..d45f277a0a8 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,6 +1,4 @@ -use std::collections::BTreeMap; use std::mem; -use std::path::Path; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; @@ -9,6 +7,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; use crate::clean::{self, GetDefId, ItemId}; +use crate::config::RenderOptions; use crate::fold::DocFolder; use crate::formats::item_type::ItemType; use crate::formats::Impl; @@ -142,19 +141,21 @@ impl Cache { &mut self, mut krate: clean::Crate, tcx: TyCtxt<'_>, - extern_html_root_urls: &BTreeMap<String, String>, - dst: &Path, + render_options: &RenderOptions, ) -> clean::Crate { // Crawl the crate to build various caches used for the output debug!(?self.crate_version); self.traits = krate.external_traits.take(); + let RenderOptions { extern_html_root_takes_precedence, output: dst, .. } = render_options; // Cache where all our extern crates are located // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code for &e in &krate.externs { let name = e.name(tcx); - let extern_url = extern_html_root_urls.get(&*name.as_str()).map(|u| &**u); - self.extern_locations.insert(e.crate_num, e.location(extern_url, &dst, tcx)); + let extern_url = + render_options.extern_html_root_urls.get(&*name.as_str()).map(|u| &**u); + let location = e.location(extern_url, *extern_html_root_takes_precedence, dst, tcx); + self.extern_locations.insert(e.crate_num, location); self.external_paths.insert(e.def_id(), (vec![name.to_string()], ItemType::Module)); } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 3cdb1352bef..f8fc9243e14 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -65,10 +65,11 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf out.push_buffer(extra); } if let Some(class) = class { - writeln!(out, "<pre class=\"rust {}\">", class); + write!(out, "<pre class=\"rust {}\">", class); } else { - writeln!(out, "<pre class=\"rust\">"); + write!(out, "<pre class=\"rust\">"); } + write!(out, "<code>"); } /// Convert the given `src` source code into HTML by adding classes for highlighting. @@ -101,7 +102,7 @@ fn write_code( } fn write_footer(out: &mut Buffer, playground_button: Option<&str>) { - writeln!(out, "</pre>{}</div>", playground_button.unwrap_or_default()); + writeln!(out, "</code></pre>{}</div>", playground_button.unwrap_or_default()); } /// How a span of text is classified. Mostly corresponds to token kinds. diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 472323daf30..7c6d7dff816 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -234,7 +234,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { return Some(Event::Html( format!( "<div class=\"example-wrap\">\ - <pre class=\"language-{}\">{}</pre>\ + <pre class=\"language-{}\"><code>{}</code></pre>\ </div>", lang, Escape(&text), diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index a3b01a59f27..96cc67ce97c 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -454,24 +454,25 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean:: + name.as_str().len() + generics_len; - w.write_str("<pre class=\"rust fn\">"); - render_attributes_in_pre(w, it, ""); - w.reserve(header_len); - write!( - w, - "{vis}{constness}{asyncness}{unsafety}{abi}fn \ - {name}{generics}{decl}{notable_traits}{where_clause}</pre>", - vis = vis, - constness = constness, - asyncness = asyncness, - unsafety = unsafety, - abi = abi, - name = name, - generics = f.generics.print(cx), - where_clause = print_where_clause(&f.generics, cx, 0, true), - decl = f.decl.full_print(header_len, 0, f.header.asyncness, cx), - notable_traits = notable_traits_decl(&f.decl, cx), - ); + wrap_item(w, "fn", |w| { + render_attributes_in_pre(w, it, ""); + w.reserve(header_len); + write!( + w, + "{vis}{constness}{asyncness}{unsafety}{abi}fn \ + {name}{generics}{decl}{notable_traits}{where_clause}", + vis = vis, + constness = constness, + asyncness = asyncness, + unsafety = unsafety, + abi = abi, + name = name, + generics = f.generics.print(cx), + where_clause = print_where_clause(&f.generics, cx, 0, true), + decl = f.decl.full_print(header_len, 0, f.header.asyncness, cx), + notable_traits = notable_traits_decl(&f.decl, cx), + ); + }); document(w, cx, it, None) } @@ -487,108 +488,111 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra // Output the trait definition wrap_into_docblock(w, |w| { - w.write_str("<pre class=\"rust trait\">"); - render_attributes_in_pre(w, it, ""); - write!( - w, - "{}{}{}trait {}{}{}", - it.visibility.print_with_space(it.def_id, cx), - t.unsafety.print_with_space(), - if t.is_auto { "auto " } else { "" }, - it.name.as_ref().unwrap(), - t.generics.print(cx), - bounds - ); - - if !t.generics.where_predicates.is_empty() { - write!(w, "{}", print_where_clause(&t.generics, cx, 0, true)); - } else { - w.write_str(" "); - } + wrap_item(w, "trait", |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "{}{}{}trait {}{}{}", + it.visibility.print_with_space(it.def_id, cx), + t.unsafety.print_with_space(), + if t.is_auto { "auto " } else { "" }, + it.name.as_ref().unwrap(), + t.generics.print(cx), + bounds + ); - if t.items.is_empty() { - w.write_str("{ }"); - } else { - // FIXME: we should be using a derived_id for the Anchors here - w.write_str("{\n"); - let mut toggle = false; - - // If there are too many associated types, hide _everything_ - if should_hide_fields(count_types) { - toggle = true; - toggle_open( - w, - format_args!("{} associated items", count_types + count_consts + count_methods), - ); - } - for t in &types { - render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx); - w.write_str(";\n"); - } - // If there are too many associated constants, hide everything after them - // We also do this if the types + consts is large because otherwise we could - // render a bunch of types and _then_ a bunch of consts just because both were - // _just_ under the limit - if !toggle && should_hide_fields(count_types + count_consts) { - toggle = true; - toggle_open( - w, - format_args!( - "{} associated constant{} and {} method{}", - count_consts, - pluralize(count_consts), - count_methods, - pluralize(count_methods), - ), - ); - } - if !types.is_empty() && !consts.is_empty() { - w.write_str("\n"); - } - for t in &consts { - render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx); - w.write_str(";\n"); - } - if !toggle && should_hide_fields(count_methods) { - toggle = true; - toggle_open(w, format_args!("{} methods", count_methods)); - } - if !consts.is_empty() && !required.is_empty() { - w.write_str("\n"); + if !t.generics.where_predicates.is_empty() { + write!(w, "{}", print_where_clause(&t.generics, cx, 0, true)); + } else { + w.write_str(" "); } - for (pos, m) in required.iter().enumerate() { - render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx); - w.write_str(";\n"); - if pos < required.len() - 1 { - w.write_str("<div class=\"item-spacer\"></div>"); + if t.items.is_empty() { + w.write_str("{ }"); + } else { + // FIXME: we should be using a derived_id for the Anchors here + w.write_str("{\n"); + let mut toggle = false; + + // If there are too many associated types, hide _everything_ + if should_hide_fields(count_types) { + toggle = true; + toggle_open( + w, + format_args!( + "{} associated items", + count_types + count_consts + count_methods + ), + ); } - } - if !required.is_empty() && !provided.is_empty() { - w.write_str("\n"); - } - for (pos, m) in provided.iter().enumerate() { - render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx); - match *m.kind { - clean::MethodItem(ref inner, _) - if !inner.generics.where_predicates.is_empty() => - { - w.write_str(",\n { ... }\n"); + for t in &types { + render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx); + w.write_str(";\n"); + } + // If there are too many associated constants, hide everything after them + // We also do this if the types + consts is large because otherwise we could + // render a bunch of types and _then_ a bunch of consts just because both were + // _just_ under the limit + if !toggle && should_hide_fields(count_types + count_consts) { + toggle = true; + toggle_open( + w, + format_args!( + "{} associated constant{} and {} method{}", + count_consts, + pluralize(count_consts), + count_methods, + pluralize(count_methods), + ), + ); + } + if !types.is_empty() && !consts.is_empty() { + w.write_str("\n"); + } + for t in &consts { + render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx); + w.write_str(";\n"); + } + if !toggle && should_hide_fields(count_methods) { + toggle = true; + toggle_open(w, format_args!("{} methods", count_methods)); + } + if !consts.is_empty() && !required.is_empty() { + w.write_str("\n"); + } + for (pos, m) in required.iter().enumerate() { + render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx); + w.write_str(";\n"); + + if pos < required.len() - 1 { + w.write_str("<div class=\"item-spacer\"></div>"); } - _ => { - w.write_str(" { ... }\n"); + } + if !required.is_empty() && !provided.is_empty() { + w.write_str("\n"); + } + for (pos, m) in provided.iter().enumerate() { + render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx); + match *m.kind { + clean::MethodItem(ref inner, _) + if !inner.generics.where_predicates.is_empty() => + { + w.write_str(",\n { ... }\n"); + } + _ => { + w.write_str(" { ... }\n"); + } + } + if pos < provided.len() - 1 { + w.write_str("<div class=\"item-spacer\"></div>"); } } - if pos < provided.len() - 1 { - w.write_str("<div class=\"item-spacer\"></div>"); + if toggle { + toggle_close(w); } + w.write_str("}"); } - if toggle { - toggle_close(w); - } - w.write_str("}"); - } - w.write_str("</pre>") + }); }); // Trait documentation @@ -811,16 +815,17 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra } fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { - w.write_str("<pre class=\"rust trait-alias\">"); - render_attributes_in_pre(w, it, ""); - write!( - w, - "trait {}{}{} = {};</pre>", - it.name.as_ref().unwrap(), - t.generics.print(cx), - print_where_clause(&t.generics, cx, 0, true), - bounds(&t.bounds, true, cx) - ); + wrap_item(w, "trait-alias", |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "trait {}{}{} = {};", + it.name.as_ref().unwrap(), + t.generics.print(cx), + print_where_clause(&t.generics, cx, 0, true), + bounds(&t.bounds, true, cx) + ); + }); document(w, cx, it, None); @@ -832,16 +837,17 @@ fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clea } fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { - w.write_str("<pre class=\"rust opaque\">"); - render_attributes_in_pre(w, it, ""); - write!( - w, - "type {}{}{where_clause} = impl {bounds};</pre>", - it.name.as_ref().unwrap(), - t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, true), - bounds = bounds(&t.bounds, false, cx), - ); + wrap_item(w, "opaque", |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "type {}{}{where_clause} = impl {bounds};", + it.name.as_ref().unwrap(), + t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, true), + bounds = bounds(&t.bounds, false, cx), + ); + }); document(w, cx, it, None); @@ -859,19 +865,20 @@ fn item_typedef( t: &clean::Typedef, is_associated: bool, ) { - w.write_str("<pre class=\"rust typedef\">"); - render_attributes_in_pre(w, it, ""); - if !is_associated { - write!(w, "{}", it.visibility.print_with_space(it.def_id, cx)); - } - write!( - w, - "type {}{}{where_clause} = {type_};</pre>", - it.name.as_ref().unwrap(), - t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, true), - type_ = t.type_.print(cx), - ); + wrap_item(w, "typedef", |w| { + render_attributes_in_pre(w, it, ""); + if !is_associated { + write!(w, "{}", it.visibility.print_with_space(it.def_id, cx)); + } + write!( + w, + "type {}{}{where_clause} = {type_};", + it.name.as_ref().unwrap(), + t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, true), + type_ = t.type_.print(cx), + ); + }); document(w, cx, it, None); @@ -885,10 +892,10 @@ fn item_typedef( fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) { wrap_into_docblock(w, |w| { - w.write_str("<pre class=\"rust union\">"); - render_attributes_in_pre(w, it, ""); - render_union(w, it, Some(&s.generics), &s.fields, "", cx); - w.write_str("</pre>") + wrap_item(w, "union", |w| { + render_attributes_in_pre(w, it, ""); + render_union(w, it, Some(&s.generics), &s.fields, "", cx); + }); }); document(w, cx, it, None); @@ -934,59 +941,68 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) { wrap_into_docblock(w, |w| { - w.write_str("<pre class=\"rust enum\">"); - render_attributes_in_pre(w, it, ""); - write!( - w, - "{}enum {}{}{}", - it.visibility.print_with_space(it.def_id, cx), - it.name.as_ref().unwrap(), - e.generics.print(cx), - print_where_clause(&e.generics, cx, 0, true), - ); - if e.variants.is_empty() && !e.variants_stripped { - w.write_str(" {}"); - } else { - w.write_str(" {\n"); - let count_variants = e.variants.len(); - let toggle = should_hide_fields(count_variants); - if toggle { - toggle_open(w, format_args!("{} variants", count_variants)); - } - for v in &e.variants { - w.write_str(" "); - let name = v.name.as_ref().unwrap(); - match *v.kind { - clean::VariantItem(ref var) => match var { - clean::Variant::CLike => write!(w, "{}", name), - clean::Variant::Tuple(ref tys) => { - write!(w, "{}(", name); - for (i, ty) in tys.iter().enumerate() { - if i > 0 { - w.write_str(", ") + wrap_item(w, "enum", |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "{}enum {}{}{}", + it.visibility.print_with_space(it.def_id, cx), + it.name.as_ref().unwrap(), + e.generics.print(cx), + print_where_clause(&e.generics, cx, 0, true), + ); + if e.variants.is_empty() && !e.variants_stripped { + w.write_str(" {}"); + } else { + w.write_str(" {\n"); + let count_variants = e.variants.len(); + let toggle = should_hide_fields(count_variants); + if toggle { + toggle_open(w, format_args!("{} variants", count_variants)); + } + for v in &e.variants { + w.write_str(" "); + let name = v.name.as_ref().unwrap(); + match *v.kind { + clean::VariantItem(ref var) => match var { + clean::Variant::CLike => write!(w, "{}", name), + clean::Variant::Tuple(ref tys) => { + write!(w, "{}(", name); + for (i, ty) in tys.iter().enumerate() { + if i > 0 { + w.write_str(", ") + } + write!(w, "{}", ty.print(cx)); } - write!(w, "{}", ty.print(cx)); + w.write_str(")"); } - w.write_str(")"); - } - clean::Variant::Struct(ref s) => { - render_struct(w, v, None, s.struct_type, &s.fields, " ", false, cx); - } - }, - _ => unreachable!(), + clean::Variant::Struct(ref s) => { + render_struct( + w, + v, + None, + s.struct_type, + &s.fields, + " ", + false, + cx, + ); + } + }, + _ => unreachable!(), + } + w.write_str(",\n"); } - w.write_str(",\n"); - } - if e.variants_stripped { - w.write_str(" // some variants omitted\n"); - } - if toggle { - toggle_close(w); + if e.variants_stripped { + w.write_str(" // some variants omitted\n"); + } + if toggle { + toggle_close(w); + } + w.write_str("}"); } - w.write_str("}"); - } - w.write_str("</pre>") + }); }); document(w, cx, it, None); @@ -1090,27 +1106,27 @@ fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean let name = it.name.as_ref().expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - w.push_str("<pre class=\"rust macro\">"); - write!(w, "{}!() {{ /* proc-macro */ }}", name); - w.push_str("</pre>"); + wrap_item(w, "macro", |w| { + write!(w, "{}!() {{ /* proc-macro */ }}", name); + }); } MacroKind::Attr => { - w.push_str("<pre class=\"rust attr\">"); - write!(w, "#[{}]", name); - w.push_str("</pre>"); + wrap_item(w, "attr", |w| { + write!(w, "#[{}]", name); + }); } MacroKind::Derive => { - w.push_str("<pre class=\"rust derive\">"); - write!(w, "#[derive({})]", name); - if !m.helpers.is_empty() { - w.push_str("\n{\n"); - w.push_str(" // Attributes available to this derive:\n"); - for attr in &m.helpers { - writeln!(w, " #[{}]", attr); + wrap_item(w, "derive", |w| { + write!(w, "#[derive({})]", name); + if !m.helpers.is_empty() { + w.push_str("\n{\n"); + w.push_str(" // Attributes available to this derive:\n"); + for attr in &m.helpers { + writeln!(w, " #[{}]", attr); + } + w.push_str("}\n"); } - w.push_str("}\n"); - } - w.push_str("</pre>"); + }); } } document(w, cx, it, None) @@ -1122,49 +1138,49 @@ fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { } fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) { - w.write_str("<pre class=\"rust const\">"); - render_attributes_in_code(w, it); + wrap_item(w, "const", |w| { + render_attributes_in_code(w, it); - write!( - w, - "{vis}const {name}: {typ}", - vis = it.visibility.print_with_space(it.def_id, cx), - name = it.name.as_ref().unwrap(), - typ = c.type_.print(cx), - ); + write!( + w, + "{vis}const {name}: {typ}", + vis = it.visibility.print_with_space(it.def_id, cx), + name = it.name.as_ref().unwrap(), + typ = c.type_.print(cx), + ); - let value = c.value(cx.tcx()); - let is_literal = c.is_literal(cx.tcx()); - let expr = c.expr(cx.tcx()); - if value.is_some() || is_literal { - write!(w, " = {expr};", expr = Escape(&expr)); - } else { - w.write_str(";"); - } + let value = c.value(cx.tcx()); + let is_literal = c.is_literal(cx.tcx()); + let expr = c.expr(cx.tcx()); + if value.is_some() || is_literal { + write!(w, " = {expr};", expr = Escape(&expr)); + } else { + w.write_str(";"); + } - if !is_literal { - if let Some(value) = &value { - let value_lowercase = value.to_lowercase(); - let expr_lowercase = expr.to_lowercase(); + if !is_literal { + if let Some(value) = &value { + let value_lowercase = value.to_lowercase(); + let expr_lowercase = expr.to_lowercase(); - if value_lowercase != expr_lowercase - && value_lowercase.trim_end_matches("i32") != expr_lowercase - { - write!(w, " // {value}", value = Escape(value)); + if value_lowercase != expr_lowercase + && value_lowercase.trim_end_matches("i32") != expr_lowercase + { + write!(w, " // {value}", value = Escape(value)); + } } } - } + }); - w.write_str("</pre>"); document(w, cx, it, None) } fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) { wrap_into_docblock(w, |w| { - w.write_str("<pre class=\"rust struct\">"); - render_attributes_in_code(w, it); - render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx); - w.write_str("</pre>") + wrap_item(w, "struct", |w| { + render_attributes_in_code(w, it); + render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx); + }); }); document(w, cx, it, None); @@ -1213,28 +1229,31 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St } fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) { - w.write_str("<pre class=\"rust static\">"); - render_attributes_in_code(w, it); - write!( - w, - "{vis}static {mutability}{name}: {typ}</pre>", - vis = it.visibility.print_with_space(it.def_id, cx), - mutability = s.mutability.print_with_space(), - name = it.name.as_ref().unwrap(), - typ = s.type_.print(cx) - ); + wrap_item(w, "static", |w| { + render_attributes_in_code(w, it); + write!( + w, + "{vis}static {mutability}{name}: {typ}", + vis = it.visibility.print_with_space(it.def_id, cx), + mutability = s.mutability.print_with_space(), + name = it.name.as_ref().unwrap(), + typ = s.type_.print(cx) + ); + }); document(w, cx, it, None) } fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { - w.write_str("<pre class=\"rust foreigntype\">extern {\n"); - render_attributes_in_code(w, it); - write!( - w, - " {}type {};\n}}</pre>", - it.visibility.print_with_space(it.def_id, cx), - it.name.as_ref().unwrap(), - ); + wrap_item(w, "foreigntype", |w| { + w.write_str("extern {\n"); + render_attributes_in_code(w, it); + write!( + w, + " {}type {};\n}}", + it.visibility.print_with_space(it.def_id, cx), + it.name.as_ref().unwrap(), + ); + }); document(w, cx, it, None); @@ -1321,6 +1340,15 @@ where w.write_str("</div>") } +fn wrap_item<F>(w: &mut Buffer, item_name: &str, f: F) +where + F: FnOnce(&mut Buffer), +{ + w.write_fmt(format_args!("<pre class=\"rust {}\"><code>", item_name)); + f(w); + w.write_str("</code></pre>"); +} + fn render_stability_since( w: &mut Buffer, item: &clean::Item, diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 0714de9d565..23ca6eeaf3b 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -249,7 +249,6 @@ code, pre, a.test-arrow, .code-header { } .docblock pre code, .docblock-short pre code { padding: 0; - padding-right: 1ex; } pre { padding: 14px; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index e02d92b11b8..34fe808dae2 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -294,6 +294,13 @@ fn opts() -> Vec<RustcOptGroup> { "NAME=URL", ) }), + unstable("extern-html-root-takes-precedence", |o| { + o.optflagmulti( + "", + "extern-html-root-takes-precedence", + "give precedence to `--extern-html-root-url`, not `html_root_url`", + ) + }), stable("plugin-path", |o| o.optmulti("", "plugin-path", "removed", "DIR")), stable("C", |o| { o.optmulti("C", "codegen", "pass a codegen option to rustc", "OPT[=VALUE]") diff --git a/src/llvm-project b/src/llvm-project -Subproject 260e0f8682098faab68af9c608534756ad37836 +Subproject e6c5dd473b7da567dc20ed3a635ca4dd2c6b7fb diff --git a/src/test/codegen/array-equality.rs b/src/test/codegen/array-equality.rs index 4b60fa4b0bf..fefc232b490 100644 --- a/src/test/codegen/array-equality.rs +++ b/src/test/codegen/array-equality.rs @@ -29,7 +29,7 @@ pub fn array_eq_value_still_passed_by_pointer(a: [u16; 9], b: [u16; 9]) -> bool // CHECK-NEXT: start: // CHECK-NEXT: bitcast // CHECK-NEXT: bitcast - // CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* nonnull dereferenceable(18) %{{.+}}, i8* nonnull dereferenceable(18) %{{.+}}, i64 18) + // CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* {{.*}} dereferenceable(18) %{{.+}}, i8* {{.*}} dereferenceable(18) %{{.+}}, i64 18) // CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0 // CHECK-NEXT: ret i1 %[[EQ]] a == b @@ -41,7 +41,7 @@ pub fn array_eq_long(a: &[u16; 1234], b: &[u16; 1234]) -> bool { // CHECK-NEXT: start: // CHECK-NEXT: bitcast // CHECK-NEXT: bitcast - // CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* nonnull dereferenceable(2468) %{{.+}}, i8* nonnull dereferenceable(2468) %{{.+}}, i64 2468) + // CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* {{.*}} dereferenceable(2468) %{{.+}}, i8* {{.*}} dereferenceable(2468) %{{.+}}, i64 2468) // CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0 // CHECK-NEXT: ret i1 %[[EQ]] a == b diff --git a/src/test/codegen/issue-83623-SIMD-PartialEq.rs b/src/test/codegen/issue-83623-SIMD-PartialEq.rs deleted file mode 100644 index b22b7f52402..00000000000 --- a/src/test/codegen/issue-83623-SIMD-PartialEq.rs +++ /dev/null @@ -1,46 +0,0 @@ -// This test checks that jumps generated by logical operators can be optimized away - -// compile-flags: -Copt-level=3 -// only-64bit - -#![crate_type="lib"] - -pub struct Blueprint { - pub fuel_tank_size: u32, - pub payload: u32, - pub wheel_diameter: u32, - pub wheel_width: u32, - pub storage: u32, -} - -// && chains should not prevent SIMD optimizations for primitives -impl PartialEq for Blueprint{ - fn eq(&self, other: &Self)->bool{ - // CHECK-NOT: call{{.*}}bcmp - // CHECK-NOT: call{{.*}}memcmp - // CHECK-NOT: br {{.*}} - self.fuel_tank_size == other.fuel_tank_size - && self.payload == other.payload - && self.wheel_diameter == other.wheel_diameter - && self.wheel_width == other.wheel_width - && self.storage == other.storage - } -} - -#[derive(PartialEq)] -pub struct Blueprint2 { - pub fuel_tank_size: u32, - pub payload: u32, - pub wheel_diameter: u32, - pub wheel_width: u32, - pub storage: u32, -} - -// Derived PartialEq should not generate jumps and should use SIMD -#[no_mangle] -pub fn partial_eq_should_not_jump(a: &Blueprint2, b:&Blueprint2)->bool{ - // CHECK-NOT: call{{.*}}bcmp - // CHECK-NOT: call{{.*}}memcmp - // CHECK-NOT: br {{.*}} - a==b -} diff --git a/src/test/codegen/naked-noinline.rs b/src/test/codegen/naked-noinline.rs index d9e6f6c34ec..d576a53826c 100644 --- a/src/test/codegen/naked-noinline.rs +++ b/src/test/codegen/naked-noinline.rs @@ -1,5 +1,6 @@ // Checks that naked functions are never inlined. // compile-flags: -O -Zmir-opt-level=3 +// needs-asm-support // ignore-wasm32 #![crate_type = "lib"] #![feature(asm)] diff --git a/src/test/codegen/repeat-trusted-len.rs b/src/test/codegen/repeat-trusted-len.rs index 9e904fc82ab..cb2d0ef809a 100644 --- a/src/test/codegen/repeat-trusted-len.rs +++ b/src/test/codegen/repeat-trusted-len.rs @@ -8,6 +8,6 @@ use std::iter; // CHECK-LABEL: @repeat_take_collect #[no_mangle] pub fn repeat_take_collect() -> Vec<u8> { -// CHECK: call void @llvm.memset.p0i8.i{{[0-9]+}}(i8* {{(nonnull )?}}align 1{{.*}} %{{[0-9]+}}, i8 42, i{{[0-9]+}} 100000, i1 false) +// CHECK: call void @llvm.memset.p0i8.i{{[0-9]+}}(i8* {{.*}}align 1{{.*}} %{{[0-9]+}}, i8 42, i{{[0-9]+}} 100000, i1 false) iter::repeat(42).take(100000).collect() } diff --git a/src/test/incremental/const-generics/hash-tyvid-regression-1.rs b/src/test/incremental/const-generics/hash-tyvid-regression-1.rs new file mode 100644 index 00000000000..f98ae59ddfe --- /dev/null +++ b/src/test/incremental/const-generics/hash-tyvid-regression-1.rs @@ -0,0 +1,15 @@ +// revisions: cfail +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] +// regression test for #77650 +fn c<T, const N: std::num::NonZeroUsize>() +where + [T; N.get()]: Sized, +{ + use std::convert::TryFrom; + <[T; N.get()]>::try_from(()) + //~^ error: the trait bound + //~^^ error: mismatched types +} + +fn main() {} diff --git a/src/test/incremental/const-generics/hash-tyvid-regression-1.stderr b/src/test/incremental/const-generics/hash-tyvid-regression-1.stderr new file mode 100644 index 00000000000..cb8ca3abd7f --- /dev/null +++ b/src/test/incremental/const-generics/hash-tyvid-regression-1.stderr @@ -0,0 +1,35 @@ +error[E0277]: the trait bound `[T; _]: From<()>` is not satisfied + --> $DIR/hash-tyvid-regression-1.rs:9:5 + | +LL | <[T; N.get()]>::try_from(()) + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<()>` is not implemented for `[T; _]` + | + = note: required because of the requirements on the impl of `Into<[T; _]>` for `()` + = note: required because of the requirements on the impl of `TryFrom<()>` for `[T; _]` +note: required by `try_from` + --> $SRC_DIR/core/src/convert/mod.rs:LL:COL + | +LL | fn try_from(value: T) -> Result<Self, Self::Error>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/hash-tyvid-regression-1.rs:9:5 + | +LL | <[T; N.get()]>::try_from(()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found enum `Result` + | + = note: expected unit type `()` + found enum `Result<[T; _], Infallible>` +help: consider using a semicolon here + | +LL | <[T; N.get()]>::try_from(()); + | + +help: try adding a return type + | +LL | -> Result<[T; _], Infallible> where + | +++++++++++++++++++++++++++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/incremental/const-generics/hash-tyvid-regression-2.rs b/src/test/incremental/const-generics/hash-tyvid-regression-2.rs new file mode 100644 index 00000000000..22536ff56d7 --- /dev/null +++ b/src/test/incremental/const-generics/hash-tyvid-regression-2.rs @@ -0,0 +1,18 @@ +// revisions: cfail +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] +// regression test for #77650 +struct C<T, const N: core::num::NonZeroUsize>([T; N.get()]) +where + [T; N.get()]: Sized; +impl<'a, const N: core::num::NonZeroUsize, A, B: PartialEq<A>> PartialEq<&'a [A]> for C<B, N> +where + [B; N.get()]: Sized, +{ + fn eq(&self, other: &&'a [A]) -> bool { + self.0 == other + //~^ error: can't compare + } +} + +fn main() {} diff --git a/src/test/incremental/const-generics/hash-tyvid-regression-2.stderr b/src/test/incremental/const-generics/hash-tyvid-regression-2.stderr new file mode 100644 index 00000000000..0e6040ef02e --- /dev/null +++ b/src/test/incremental/const-generics/hash-tyvid-regression-2.stderr @@ -0,0 +1,11 @@ +error[E0277]: can't compare `[B; _]` with `&&[A]` + --> $DIR/hash-tyvid-regression-2.rs:12:16 + | +LL | self.0 == other + | ^^ no implementation for `[B; _] == &&[A]` + | + = help: the trait `PartialEq<&&[A]>` is not implemented for `[B; _]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/incremental/const-generics/hash-tyvid-regression-3.rs b/src/test/incremental/const-generics/hash-tyvid-regression-3.rs new file mode 100644 index 00000000000..76b1ae11c7d --- /dev/null +++ b/src/test/incremental/const-generics/hash-tyvid-regression-3.rs @@ -0,0 +1,26 @@ +// revisions: cfail +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] +// regression test for #79251 +struct Node<const D: usize> +where + SmallVec<{ D * 2 }>: , +{ + keys: SmallVec<{ D * 2 }>, +} + +impl<const D: usize> Node<D> +where + SmallVec<{ D * 2 }>: , +{ + fn new() -> Self { + let mut node = Node::new(); + node.keys.some_function(); + //~^ error: no method named + node + } +} + +struct SmallVec<const D: usize> {} + +fn main() {} diff --git a/src/test/incremental/const-generics/hash-tyvid-regression-3.stderr b/src/test/incremental/const-generics/hash-tyvid-regression-3.stderr new file mode 100644 index 00000000000..555d46756dc --- /dev/null +++ b/src/test/incremental/const-generics/hash-tyvid-regression-3.stderr @@ -0,0 +1,12 @@ +error[E0599]: no method named `some_function` found for struct `SmallVec` in the current scope + --> $DIR/hash-tyvid-regression-3.rs:17:19 + | +LL | node.keys.some_function(); + | ^^^^^^^^^^^^^ method not found in `SmallVec<{ D * 2 }>` +... +LL | struct SmallVec<const D: usize> {} + | ------------------------------- method `some_function` not found for this + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/incremental/const-generics/hash-tyvid-regression-4.rs b/src/test/incremental/const-generics/hash-tyvid-regression-4.rs new file mode 100644 index 00000000000..35a675a2ab4 --- /dev/null +++ b/src/test/incremental/const-generics/hash-tyvid-regression-4.rs @@ -0,0 +1,40 @@ +// revisions: cfail +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] +// regression test for #79251 +#[derive(Debug)] +struct Node<K, const D: usize> +where + SmallVec<K, { D * 2 }>: , +{ + keys: SmallVec<K, { D * 2 }>, +} + +impl<K, const D: usize> Node<K, D> +where + SmallVec<K, { D * 2 }>: , +{ + fn new() -> Self { + panic!() + } + + #[inline(never)] + fn split(&mut self, i: usize, k: K, right: bool) -> Node<K, D> { + let mut node = Node::new(); + node.keys.push(k); + //~^ error: no method named + node + } +} + +#[derive(Debug)] +struct SmallVec<T, const D: usize> { + data: [T; D], +} +impl<T, const D: usize> SmallVec<T, D> { + fn new() -> Self { + panic!() + } +} + +fn main() {} diff --git a/src/test/incremental/const-generics/hash-tyvid-regression-4.stderr b/src/test/incremental/const-generics/hash-tyvid-regression-4.stderr new file mode 100644 index 00000000000..c9a6715e571 --- /dev/null +++ b/src/test/incremental/const-generics/hash-tyvid-regression-4.stderr @@ -0,0 +1,12 @@ +error[E0599]: no method named `push` found for struct `SmallVec` in the current scope + --> $DIR/hash-tyvid-regression-4.rs:23:19 + | +LL | node.keys.push(k); + | ^^^^ method not found in `SmallVec<_, { D * 2 }>` +... +LL | struct SmallVec<T, const D: usize> { + | ---------------------------------- method `push` not found for this + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-1.rs b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-1.rs new file mode 100644 index 00000000000..a6edbb2e9f9 --- /dev/null +++ b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-1.rs @@ -0,0 +1,23 @@ +// revisions: cfail +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features, unused_braces)] + +trait Delegates<T> {} + +struct FileCap<const Op: bool> {} + +fn writes_to_path<C>(cap: &C) +where + C: Delegates<FileCap<{ false }>>, +{ + writes_to_specific_path(&cap); + //~^ error: the trait bound +} + +fn writes_to_specific_path<C>(cap: &C) +where + C: Delegates<FileCap<{ false }>>, +{ +} + +fn main() {} diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-2.rs b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-2.rs new file mode 100644 index 00000000000..52eeec73b0d --- /dev/null +++ b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-2.rs @@ -0,0 +1,18 @@ +// revisions: rpass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +struct Z; +const fn one() -> usize { + 1 +} + +fn from_a_to_b<T>(source: [u8; one()]) -> T { + todo!() +} + +fn not_main() { + let _: &Z = from_a_to_b([0; 1]); +} + +fn main() {} diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs new file mode 100644 index 00000000000..11cca942dd6 --- /dev/null +++ b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs @@ -0,0 +1,22 @@ +// revisions: rpass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +use std::{convert::TryFrom, num::NonZeroUsize}; + +struct A<const N: NonZeroUsize>([u8; N.get()]) +where + [u8; N.get()]: Sized; + +impl<'a, const N: NonZeroUsize> TryFrom<&'a [u8]> for A<N> +where + [u8; N.get()]: Sized, +{ + type Error = (); + fn try_from(slice: &'a [u8]) -> Result<A<N>, ()> { + let _x = <&[u8; N.get()] as TryFrom<&[u8]>>::try_from(slice); + unimplemented!(); + } +} + +fn main() {} diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-82034.rs b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-82034.rs new file mode 100644 index 00000000000..1b769d0a30d --- /dev/null +++ b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-82034.rs @@ -0,0 +1,34 @@ +// revisions: rpass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] +pub trait IsTrue {} +pub trait IsFalse {} + +pub struct Assert<const CHECK: bool> {} + +impl IsTrue for Assert<true> {} +impl IsFalse for Assert<false> {} + +pub struct SliceConstWriter<'a, const N: usize> { + ptr: &'a mut [u8], +} +impl<'a, const N: usize> SliceConstWriter<'a, { N }> { + pub fn from_slice(vec: &'a mut [u8]) -> Self { + Self { ptr: vec } + } + + pub fn convert<const NN: usize>(mut self) -> SliceConstWriter<'a, { NN }> { + SliceConstWriter { ptr: self.ptr } + } +} + +impl<'a, const N: usize> SliceConstWriter<'a, { N }> +where + Assert<{ N >= 2 }>: IsTrue, +{ + pub fn write_u8(mut self) -> SliceConstWriter<'a, { N - 2 }> { + self.convert() + } +} + +fn main() {} diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-1.rs b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-1.rs new file mode 100644 index 00000000000..20d4993774d --- /dev/null +++ b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-1.rs @@ -0,0 +1,23 @@ +// revisions: rpass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +pub struct Ref<'a, const NUM: usize>(&'a i32); + +impl<'a, const NUM: usize> Ref<'a, NUM> { + pub fn foo<const A: usize>(r: Ref<'a, A>) -> Self + where + ([(); NUM - A], [(); A - NUM]): Sized, + { + Self::bar(r) + } + + pub fn bar<const A: usize>(r: Ref<'a, A>) -> Self + where + ([(); NUM - A], [(); A - NUM]): Sized, + { + Self(r.0) + } +} + +fn main() {} diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-2.rs b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-2.rs new file mode 100644 index 00000000000..be7ad6d1fa0 --- /dev/null +++ b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-2.rs @@ -0,0 +1,14 @@ +// revisions: cfail +#![allow(incomplete_features)] +#![feature(const_generics, const_evaluatable_checked)] + +pub struct Ref<'a>(&'a i32); + +impl<'a> Ref<'a> { + pub fn foo<const A: usize>() -> [(); A - 0] { + Self::foo() + //~^ error: type annotations needed + } +} + +fn main() {} diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-3.rs b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-3.rs new file mode 100644 index 00000000000..af4f209c904 --- /dev/null +++ b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-85031-3.rs @@ -0,0 +1,25 @@ +// revisions: rpass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +fn test<const SIZE: usize>() {} + +trait SomeTrait { + const SIZE: usize; +} + +struct A<'a, T> { + some_ref: &'a str, + _maker: core::marker::PhantomData<T>, +} + +impl<'a, T: SomeTrait> A<'a, T> +where + [(); T::SIZE]: , +{ + fn call_test() { + test::<{ T::SIZE }>(); + } +} + +fn main() {} diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-86953.rs b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-86953.rs new file mode 100644 index 00000000000..ba863369626 --- /dev/null +++ b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-86953.rs @@ -0,0 +1,16 @@ +// revisions: rpass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +struct Foo; +impl<'a> std::ops::Add<&'a Foo> for Foo +where + [(); 0 + 0]: Sized, +{ + type Output = (); + fn add(self, _: &Foo) -> Self::Output { + loop {} + } +} + +fn main() {} diff --git a/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs new file mode 100644 index 00000000000..da713631cc1 --- /dev/null +++ b/src/test/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-88022.rs @@ -0,0 +1,28 @@ +// revisions: cfail +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features, unused_braces)] + +struct Buffer<T, const S: usize> +where + [(); { S * 2 }]: Default, +{ + data: [T; { S * 2 }], +} + +struct BufferIter<'a, T, const S: usize>(&'a Buffer<T, S>) +where + [(); { S * 2 }]: Default; + +impl<'a, T, const S: usize> Iterator for BufferIter<'a, T, S> { + //~^ error: the trait bound + //~^^ error: unconstrained generic constant + type Item = &'a T; + + fn next(&mut self) -> Option<Self::Item> { + //~^ error: the trait bound + //~^^ error: unconstrained generic constant + None + } +} + +fn main() {} diff --git a/src/test/incremental/hashes/trait_impls.rs b/src/test/incremental/hashes/trait_impls.rs index c9a3de1f6ae..2d547f1748c 100644 --- a/src/test/incremental/hashes/trait_impls.rs +++ b/src/test/incremental/hashes/trait_impls.rs @@ -62,7 +62,7 @@ impl ChangeMethodBodyTrait for Foo { #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl ChangeMethodBodyTrait for Foo { - #[rustc_clean(except="hir_owner_nodes,typeck,optimized_mir", cfg="cfail2")] + #[rustc_clean(except="hir_owner_nodes,typeck", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] fn method_name() { () diff --git a/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff b/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff index 19af626c49f..1b7c51b3954 100644 --- a/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff +++ b/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff @@ -29,7 +29,7 @@ } bb2: { - _0 = const (); // scope 0 at $DIR/control-flow-simplification.rs:14:6: 14:6 + nop; // scope 0 at $DIR/control-flow-simplification.rs:14:6: 14:6 StorageDead(_1); // scope 0 at $DIR/control-flow-simplification.rs:14:5: 14:6 return; // scope 0 at $DIR/control-flow-simplification.rs:15:2: 15:2 } diff --git a/src/test/mir-opt/const_prop/control_flow_simplification.hello.PreCodegen.before.mir b/src/test/mir-opt/const_prop/control_flow_simplification.hello.PreCodegen.before.mir index c2f75e5daee..30512d0bbe8 100644 --- a/src/test/mir-opt/const_prop/control_flow_simplification.hello.PreCodegen.before.mir +++ b/src/test/mir-opt/const_prop/control_flow_simplification.hello.PreCodegen.before.mir @@ -4,7 +4,6 @@ fn hello() -> () { let mut _0: (); // return place in scope 0 at $DIR/control-flow-simplification.rs:11:14: 11:14 bb0: { - _0 = const (); // scope 0 at $DIR/control-flow-simplification.rs:14:6: 14:6 return; // scope 0 at $DIR/control-flow-simplification.rs:15:2: 15:2 } } diff --git a/src/test/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff b/src/test/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff index 8793af5c074..546a95a86fa 100644 --- a/src/test/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff +++ b/src/test/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff @@ -14,7 +14,6 @@ StorageLive(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:21:5: 21:12 StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:21:10: 21:11 _3 = move _1; // scope 0 at $DIR/remove_unneeded_drops.rs:21:10: 21:11 - nop; // scope 1 at $DIR/remove_unneeded_drops.rs:21:5: 21:12 drop(_3) -> [return: bb2, unwind: bb1]; // scope 1 at $DIR/remove_unneeded_drops.rs:21:5: 21:12 } diff --git a/src/test/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff b/src/test/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff index 2377abdb153..b42813447b1 100644 --- a/src/test/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff +++ b/src/test/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff @@ -14,7 +14,6 @@ StorageLive(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:9:5: 9:12 StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:9:10: 9:11 _3 = move _1; // scope 0 at $DIR/remove_unneeded_drops.rs:9:10: 9:11 - nop; // scope 1 at $DIR/remove_unneeded_drops.rs:9:5: 9:12 drop(_3) -> [return: bb2, unwind: bb1]; // scope 1 at $DIR/remove_unneeded_drops.rs:9:5: 9:12 } diff --git a/src/test/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff b/src/test/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff index 75bd732e3c0..ee52254f08b 100644 --- a/src/test/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff +++ b/src/test/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff @@ -14,7 +14,6 @@ StorageLive(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:4:5: 4:12 StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:4:10: 4:11 _3 = _1; // scope 0 at $DIR/remove_unneeded_drops.rs:4:10: 4:11 -- nop; // scope 1 at $DIR/remove_unneeded_drops.rs:4:5: 4:12 - drop(_3) -> bb1; // scope 1 at $DIR/remove_unneeded_drops.rs:4:5: 4:12 - } - diff --git a/src/test/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff b/src/test/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff index b3da2cfb24c..21bc90b0eb7 100644 --- a/src/test/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff +++ b/src/test/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff @@ -14,7 +14,6 @@ StorageLive(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:14:5: 14:12 StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:14:10: 14:11 _3 = _1; // scope 0 at $DIR/remove_unneeded_drops.rs:14:10: 14:11 -- nop; // scope 1 at $DIR/remove_unneeded_drops.rs:14:5: 14:12 - drop(_3) -> bb1; // scope 1 at $DIR/remove_unneeded_drops.rs:14:5: 14:12 - } - diff --git a/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff b/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff index 49a57832267..41a6fe30412 100644 --- a/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff +++ b/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff @@ -9,8 +9,8 @@ let mut _4: isize; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 let mut _5: isize; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 let _6: u8; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 - let mut _7: bool; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 - let mut _8: u8; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 +- let mut _7: bool; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 +- let mut _8: u8; // in scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 scope 1 { debug a => _6; // in scope 1 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 } @@ -26,51 +26,32 @@ StorageDead(_3); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69 StorageDead(_2); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:68: 4:69 _5 = discriminant((_1.0: std::option::Option<u8>)); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 - switchInt(move _5) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 + switchInt(move _5) -> [1_isize: bb1, otherwise: bb3]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:13: 4:20 } bb1: { - _0 = const (); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:8:6: 8:6 - goto -> bb7; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 - } - - bb2: { _4 = discriminant((_1.1: std::option::Option<T>)); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 - switchInt(move _4) -> [0_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 + switchInt(move _4) -> [0_isize: bb2, otherwise: bb3]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:22: 4:26 } - bb3: { + bb2: { StorageLive(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 _6 = (((_1.0: std::option::Option<u8>) as Some).0: u8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19 - StorageLive(_7); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 - StorageLive(_8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 - _8 = _6; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 - _7 = Gt(move _8, const 42_u8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 - StorageDead(_8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:19: 5:20 - switchInt(move _7) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 - } - - bb4: { - _0 = const (); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:21: 7:10 - goto -> bb6; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 - } - - bb5: { - _0 = const (); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:7:10: 7:10 - goto -> bb6; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:9: 7:10 +- StorageLive(_7); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 +- StorageLive(_8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 +- _8 = _6; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13 +- _7 = Gt(move _8, const 42_u8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20 +- StorageDead(_8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:19: 5:20 +- StorageDead(_7); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:7:9: 7:10 + goto -> bb3; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 } - bb6: { - StorageDead(_7); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:7:9: 7:10 - goto -> bb7; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6 - } - - bb7: { + bb3: { StorageDead(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6 - drop(_1) -> bb8; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2 + drop(_1) -> bb4; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2 } - bb8: { + bb4: { StorageDead(_1); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2 return; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:2: 9:2 } diff --git a/src/test/run-make-fulldeps/coverage-llvmir/Makefile b/src/test/run-make-fulldeps/coverage-llvmir/Makefile index 7d9121ee2f8..1ff1ffcc4b0 100644 --- a/src/test/run-make-fulldeps/coverage-llvmir/Makefile +++ b/src/test/run-make-fulldeps/coverage-llvmir/Makefile @@ -22,7 +22,6 @@ DEFINE_INTERNAL=define internal ifdef IS_WINDOWS LLVM_FILECHECK_OPTIONS=\ -check-prefixes=CHECK,WINDOWS \ - -DPRIVATE_GLOBAL='internal global' \ -DDEFINE_INTERNAL='$(DEFINE_INTERNAL)' \ -DCOMDAT_IF_SUPPORTED='$(COMDAT_IF_SUPPORTED)' \ -DINSTR_PROF_DATA='.lprfd$$M' \ @@ -36,7 +35,6 @@ ifdef IS_WINDOWS else LLVM_FILECHECK_OPTIONS=\ -check-prefixes=CHECK \ - -DPRIVATE_GLOBAL='private global' \ -DDEFINE_INTERNAL='$(DEFINE_INTERNAL)' \ -DCOMDAT_IF_SUPPORTED='$(COMDAT_IF_SUPPORTED)' \ -DINSTR_PROF_DATA='$(DATA_SECTION_PREFIX)__llvm_prf_data$(INSTR_PROF_DATA_SUFFIX)' \ diff --git a/src/test/run-make-fulldeps/coverage-llvmir/filecheck.testprog.txt b/src/test/run-make-fulldeps/coverage-llvmir/filecheck.testprog.txt index a312ec48e84..8e5f2104687 100644 --- a/src/test/run-make-fulldeps/coverage-llvmir/filecheck.testprog.txt +++ b/src/test/run-make-fulldeps/coverage-llvmir/filecheck.testprog.txt @@ -11,27 +11,25 @@ CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8 WINDOWS: @__llvm_profile_runtime = external global i32 -CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = [[PRIVATE_GLOBAL]] -CHECK-SAME: section "[[INSTR_PROF_CNTS]]", align 8 +CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global +CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8 -CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = [[PRIVATE_GLOBAL]] +CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called, -CHECK-SAME: section "[[INSTR_PROF_DATA]]", align 8 +CHECK-SAME: section "[[INSTR_PROF_DATA]]"{{.*}}, align 8 -CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main = [[PRIVATE_GLOBAL]] -CHECK-SAME: section "[[INSTR_PROF_CNTS]]", align 8 +CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main = {{private|internal}} global +CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8 -CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog4main = [[PRIVATE_GLOBAL]] +CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog4main = {{private|internal}} global CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main, -CHECK-SAME: section "[[INSTR_PROF_DATA]]", align 8 +CHECK-SAME: section "[[INSTR_PROF_DATA]]"{{.*}}, align 8 CHECK: @__llvm_prf_nm = private constant CHECK-SAME: section "[[INSTR_PROF_NAME]]", align 1 CHECK: @llvm.used = appending global CHECK-SAME: i8* bitcast ({ {{.*}} }* @__llvm_coverage_mapping to i8*) -WINDOWS-SAME: i8* bitcast (i32 ()* @__llvm_profile_runtime_user to i8*) -CHECK-SAME: i8* bitcast ({ {{.*}} }* @__profd__R{{[a-zA-Z0-9_]*}}testprog4main to i8*) CHECK-SAME: i8* getelementptr inbounds ({{.*}}* @__llvm_prf_nm, i32 0, i32 0) CHECK-SAME: section "llvm.metadata" diff --git a/src/test/run-make/const_fn_mir/dump.mir b/src/test/run-make/const_fn_mir/dump.mir index 45d018cf305..724b2630083 100644 --- a/src/test/run-make/const_fn_mir/dump.mir +++ b/src/test/run-make/const_fn_mir/dump.mir @@ -12,7 +12,6 @@ fn main() -> () { } bb1: { - _0 = const (); // scope 0 at main.rs:8:11: 10:2 return; // scope 0 at main.rs:10:2: 10:2 } } diff --git a/src/test/rustdoc-gui/code-tags.goml b/src/test/rustdoc-gui/code-tags.goml new file mode 100644 index 00000000000..200569a28d4 --- /dev/null +++ b/src/test/rustdoc-gui/code-tags.goml @@ -0,0 +1,20 @@ +// This test ensures that items and documentation code blocks are wrapped in <pre><code> +goto: file://|DOC_PATH|/test_docs/fn.foo.html +size: (1080, 600) +// There should be three doc codeblocks +// Check that their content is inside <pre><code> +assert-count: (".example-wrap pre > code", 3) +// Check that function signature is inside <pre><code> +assert: "pre.rust.fn > code" + +goto: file://|DOC_PATH|/test_docs/struct.Foo.html +assert: "pre.rust.struct > code" + +goto: file://|DOC_PATH|/test_docs/enum.AnEnum.html +assert: "pre.rust.enum > code" + +goto: file://|DOC_PATH|/test_docs/trait.AnotherOne.html +assert: "pre.rust.trait > code" + +goto: file://|DOC_PATH|/test_docs/type.SomeType.html +assert: "pre.rust.typedef > code" diff --git a/src/test/rustdoc-gui/source-code-page.goml b/src/test/rustdoc-gui/source-code-page.goml index d7bae93c211..5a49807e180 100644 --- a/src/test/rustdoc-gui/source-code-page.goml +++ b/src/test/rustdoc-gui/source-code-page.goml @@ -12,4 +12,4 @@ assert-attribute: (".line-numbers > span:nth-child(5)", {"class": "line-highligh assert-attribute: (".line-numbers > span:nth-child(6)", {"class": "line-highlighted"}) assert-attribute-false: (".line-numbers > span:nth-child(7)", {"class": "line-highlighted"}) // This is to ensure that the content is correctly align with the line numbers. -compare-elements-position: ("//*[@id='1']", ".rust > span", ("y")) +compare-elements-position: ("//*[@id='1']", ".rust > code > span", ("y")) diff --git a/src/test/rustdoc/auxiliary/html_root.rs b/src/test/rustdoc/auxiliary/html_root.rs new file mode 100644 index 00000000000..4eb0b700f8f --- /dev/null +++ b/src/test/rustdoc/auxiliary/html_root.rs @@ -0,0 +1,2 @@ +#![doc(html_root_url="https://example.com/html_root")] +pub fn foo() {} diff --git a/src/test/rustdoc/auxiliary/no_html_root.rs b/src/test/rustdoc/auxiliary/no_html_root.rs new file mode 100644 index 00000000000..c5c0bc606cd --- /dev/null +++ b/src/test/rustdoc/auxiliary/no_html_root.rs @@ -0,0 +1 @@ +pub fn bar() {} diff --git a/src/test/rustdoc/extern-html-root-url-precedence.rs b/src/test/rustdoc/extern-html-root-url-precedence.rs new file mode 100644 index 00000000000..def6767ea47 --- /dev/null +++ b/src/test/rustdoc/extern-html-root-url-precedence.rs @@ -0,0 +1,7 @@ +// compile-flags:-Z unstable-options --extern-html-root-url core=https://example.com/core/0.1.0 --extern-html-root-takes-precedence + +// @has extern_html_root_url_precedence/index.html +// --extern-html-root should take precedence if `--takes-precedence` is passed +// @has - '//a/@href' 'https://example.com/core/0.1.0/core/iter/index.html' +#[doc(no_inline)] +pub use std::iter; diff --git a/src/test/rustdoc/extern-html-root-url.rs b/src/test/rustdoc/extern-html-root-url.rs index 60b7b28ae4a..17eedcf2ab8 100644 --- a/src/test/rustdoc/extern-html-root-url.rs +++ b/src/test/rustdoc/extern-html-root-url.rs @@ -1,6 +1,18 @@ -// compile-flags:-Z unstable-options --extern-html-root-url core=https://example.com/core/0.1.0 +// compile-flags:-Z unstable-options --extern-html-root-url html_root=https://example.com/override --extern-html-root-url no_html_root=https://example.com/override +// aux-build:html_root.rs +// aux-build:no_html_root.rs +// NOTE: intentionally does not build any auxiliary docs + +extern crate html_root; +extern crate no_html_root; // @has extern_html_root_url/index.html -// @has - '//a/@href' 'https://example.com/core/0.1.0/core/iter/index.html' +// `html_root_url` should override `--extern-html-root-url` +// @has - '//a/@href' 'https://example.com/html_root/html_root/fn.foo.html' +#[doc(no_inline)] +pub use html_root::foo; + #[doc(no_inline)] -pub use std::iter; +// `--extern-html-root-url` should apply if no `html_root_url` is given +// @has - '//a/@href' 'https://example.com/override/no_html_root/fn.bar.html' +pub use no_html_root::bar; diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87987.rs b/src/test/ui/closures/2229_closure_analysis/issue-87987.rs new file mode 100644 index 00000000000..5dc2cb7e710 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-87987.rs @@ -0,0 +1,30 @@ +// run-pass +// edition:2021 + +struct Props { + field_1: u32, //~ WARNING: field is never read: `field_1` + field_2: u32, //~ WARNING: field is never read: `field_2` +} + +fn main() { + // Test 1 + let props_2 = Props { //~ WARNING: unused variable: `props_2` + field_1: 1, + field_2: 1, + }; + + let _ = || { + let _: Props = props_2; + }; + + // Test 2 + let mut arr = [1, 3, 4, 5]; + + let mref = &mut arr; + + let _c = || match arr { + [_, _, _, _] => println!("A") + }; + + println!("{:#?}", mref); +} diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87987.stderr b/src/test/ui/closures/2229_closure_analysis/issue-87987.stderr new file mode 100644 index 00000000000..aa7012c3618 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-87987.stderr @@ -0,0 +1,24 @@ +warning: unused variable: `props_2` + --> $DIR/issue-87987.rs:11:9 + | +LL | let props_2 = Props { + | ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_props_2` + | + = note: `#[warn(unused_variables)]` on by default + +warning: field is never read: `field_1` + --> $DIR/issue-87987.rs:5:5 + | +LL | field_1: u32, + | ^^^^^^^^^^^^ + | + = note: `#[warn(dead_code)]` on by default + +warning: field is never read: `field_2` + --> $DIR/issue-87987.rs:6:5 + | +LL | field_2: u32, + | ^^^^^^^^^^^^ + +warning: 3 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87988.rs b/src/test/ui/closures/2229_closure_analysis/issue-87988.rs new file mode 100644 index 00000000000..27e7fabf11a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-87988.rs @@ -0,0 +1,19 @@ +// run-pass +// edition:2021 + +const LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: i32 = 0x01; +const LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: i32 = 0x02; + +pub fn hotplug_callback(event: i32) { + let _ = || { + match event { + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => (), + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => (), + _ => (), + }; + }; +} + +fn main() { + hotplug_callback(1); +} diff --git a/src/test/ui/closures/2229_closure_analysis/match-edge-cases.rs b/src/test/ui/closures/2229_closure_analysis/match-edge-cases.rs new file mode 100644 index 00000000000..914ebbe26a5 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match-edge-cases.rs @@ -0,0 +1,44 @@ +// run-pass +// edition:2021 + +const PATTERN_REF: &str = "Hello World"; +const NUMBER: i32 = 30; +const NUMBER_POINTER: *const i32 = &NUMBER; + +pub fn edge_case_ref(event: &str) { + let _ = || { + match event { + PATTERN_REF => (), + _ => (), + }; + }; +} + +pub fn edge_case_str(event: String) { + let _ = || { + match event.as_str() { + "hello" => (), + _ => (), + }; + }; +} + +pub fn edge_case_raw_ptr(event: *const i32) { + let _ = || { + match event { + NUMBER_POINTER => (), + _ => (), + }; + }; +} + +pub fn edge_case_char(event: char) { + let _ = || { + match event { + 'a' => (), + _ => (), + }; + }; +} + +fn main() {} diff --git a/src/test/ui/codegen/issue-88043-bb-does-not-have-terminator.rs b/src/test/ui/codegen/issue-88043-bb-does-not-have-terminator.rs index 1a6aadc662b..38dfca347c8 100644 --- a/src/test/ui/codegen/issue-88043-bb-does-not-have-terminator.rs +++ b/src/test/ui/codegen/issue-88043-bb-does-not-have-terminator.rs @@ -22,4 +22,14 @@ fn take_until(terminate: impl Fn() -> bool) { // CHECK-LABEL: @main fn main() { take_until(|| true); + f(None); } + +fn f(_a: Option<String>) -> Option<u32> { + loop { + g(); + () + } +} + +fn g() -> Option<u32> { None } diff --git a/src/test/ui/consts/const-eval/panic-assoc-never-type.rs b/src/test/ui/consts/const-eval/panic-assoc-never-type.rs index 78cf25308ff..ab0256a986d 100644 --- a/src/test/ui/consts/const-eval/panic-assoc-never-type.rs +++ b/src/test/ui/consts/const-eval/panic-assoc-never-type.rs @@ -14,5 +14,4 @@ impl PrintName { fn main() { let _ = PrintName::VOID; - //~^ ERROR erroneous constant used } diff --git a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr index 08560948309..f1c606a8a2e 100644 --- a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr +++ b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr @@ -6,12 +6,6 @@ LL | const VOID: ! = panic!(); | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0080]: erroneous constant used - --> $DIR/panic-assoc-never-type.rs:16:13 - | -LL | let _ = PrintName::VOID; - | ^^^^^^^^^^^^^^^ referenced constant has errors - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/drop-bounds/drop-bounds.stderr b/src/test/ui/drop-bounds/drop-bounds.stderr index 15ba4c9a649..3ffb855a55d 100644 --- a/src/test/ui/drop-bounds/drop-bounds.stderr +++ b/src/test/ui/drop-bounds/drop-bounds.stderr @@ -1,4 +1,4 @@ -error: bounds on `T: Drop` are useless, consider instead using `std::mem::needs_drop` to detect if a type has a destructor +error: bounds on `T: Drop` are most likely incorrect, consider instead using `std::mem::needs_drop` to detect whether a type can be trivially dropped --> $DIR/drop-bounds.rs:2:11 | LL | fn foo<T: Drop>() {} @@ -10,37 +10,37 @@ note: the lint level is defined here LL | #![deny(drop_bounds)] | ^^^^^^^^^^^ -error: bounds on `U: Drop` are useless, consider instead using `std::mem::needs_drop` to detect if a type has a destructor +error: bounds on `U: Drop` are most likely incorrect, consider instead using `std::mem::needs_drop` to detect whether a type can be trivially dropped --> $DIR/drop-bounds.rs:5:8 | LL | U: Drop, | ^^^^ -error: bounds on `impl Drop: Drop` are useless, consider instead using `std::mem::needs_drop` to detect if a type has a destructor +error: bounds on `impl Drop: Drop` are most likely incorrect, consider instead using `std::mem::needs_drop` to detect whether a type can be trivially dropped --> $DIR/drop-bounds.rs:8:17 | LL | fn baz(_x: impl Drop) {} | ^^^^ -error: bounds on `T: Drop` are useless, consider instead using `std::mem::needs_drop` to detect if a type has a destructor +error: bounds on `T: Drop` are most likely incorrect, consider instead using `std::mem::needs_drop` to detect whether a type can be trivially dropped --> $DIR/drop-bounds.rs:9:15 | LL | struct Foo<T: Drop> { | ^^^^ -error: bounds on `U: Drop` are useless, consider instead using `std::mem::needs_drop` to detect if a type has a destructor +error: bounds on `U: Drop` are most likely incorrect, consider instead using `std::mem::needs_drop` to detect whether a type can be trivially dropped --> $DIR/drop-bounds.rs:12:24 | LL | struct Bar<U> where U: Drop { | ^^^^ -error: bounds on `Self: Drop` are useless, consider instead using `std::mem::needs_drop` to detect if a type has a destructor +error: bounds on `Self: Drop` are most likely incorrect, consider instead using `std::mem::needs_drop` to detect whether a type can be trivially dropped --> $DIR/drop-bounds.rs:15:12 | LL | trait Baz: Drop { | ^^^^ -error: bounds on `T: Drop` are useless, consider instead using `std::mem::needs_drop` to detect if a type has a destructor +error: bounds on `T: Drop` are most likely incorrect, consider instead using `std::mem::needs_drop` to detect whether a type can be trivially dropped --> $DIR/drop-bounds.rs:17:9 | LL | impl<T: Drop> Baz for T { diff --git a/src/test/ui/dst/dst-rvalue.rs b/src/test/ui/dst/dst-rvalue.rs index aa028396be4..b52a95a701f 100644 --- a/src/test/ui/dst/dst-rvalue.rs +++ b/src/test/ui/dst/dst-rvalue.rs @@ -4,11 +4,9 @@ pub fn main() { let _x: Box<str> = box *"hello world"; - //~^ ERROR E0161 - //~^^ ERROR cannot move out of a shared reference + //~^ ERROR E0277 let array: &[isize] = &[1, 2, 3]; let _x: Box<[isize]> = box *array; - //~^ ERROR E0161 - //~^^ ERROR cannot move out of type `[isize]`, a non-copy slice + //~^ ERROR E0277 } diff --git a/src/test/ui/dst/dst-rvalue.stderr b/src/test/ui/dst/dst-rvalue.stderr index 6a51c517558..15830636b51 100644 --- a/src/test/ui/dst/dst-rvalue.stderr +++ b/src/test/ui/dst/dst-rvalue.stderr @@ -1,31 +1,21 @@ -error[E0161]: cannot move a value of type str: the size of str cannot be statically determined +error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/dst-rvalue.rs:6:28 | LL | let _x: Box<str> = box *"hello world"; - | ^^^^^^^^^^^^^^ - -error[E0161]: cannot move a value of type [isize]: the size of [isize] cannot be statically determined - --> $DIR/dst-rvalue.rs:11:32 + | ^^^^^^^^^^^^^^ doesn't have a size known at compile-time | -LL | let _x: Box<[isize]> = box *array; - | ^^^^^^ + = help: the trait `Sized` is not implemented for `str` + = note: the type of a box expression must have a statically known size -error[E0507]: cannot move out of a shared reference - --> $DIR/dst-rvalue.rs:6:28 - | -LL | let _x: Box<str> = box *"hello world"; - | ^^^^^^^^^^^^^^ move occurs because value has type `str`, which does not implement the `Copy` trait - -error[E0508]: cannot move out of type `[isize]`, a non-copy slice - --> $DIR/dst-rvalue.rs:11:32 +error[E0277]: the size for values of type `[isize]` cannot be known at compilation time + --> $DIR/dst-rvalue.rs:10:32 | LL | let _x: Box<[isize]> = box *array; - | ^^^^^^ - | | - | cannot move out of here - | move occurs because `*array` has type `[isize]`, which does not implement the `Copy` trait + | ^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[isize]` + = note: the type of a box expression must have a statically known size -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0161, E0507, E0508. -For more information about an error, try `rustc --explain E0161`. +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/enum/enum-discrim-autosizing.rs b/src/test/ui/enum/enum-discrim-autosizing.rs index e9bb9275971..473a0ad6f7a 100644 --- a/src/test/ui/enum/enum-discrim-autosizing.rs +++ b/src/test/ui/enum/enum-discrim-autosizing.rs @@ -4,8 +4,10 @@ // so force the repr. #[cfg_attr(not(target_pointer_width = "32"), repr(i32))] enum Eu64 { - Au64 = 0, - Bu64 = 0x8000_0000_0000_0000 //~ERROR already exists + Au64 = 0, //~NOTE first use of `0` + Bu64 = 0x8000_0000_0000_0000 + //~^ ERROR discriminant value `0` already exists + //~| NOTE enum already has `0` (overflowed from `9223372036854775808`) } fn main() {} diff --git a/src/test/ui/enum/enum-discrim-autosizing.stderr b/src/test/ui/enum/enum-discrim-autosizing.stderr index 8848f984cfb..a0f39098a2e 100644 --- a/src/test/ui/enum/enum-discrim-autosizing.stderr +++ b/src/test/ui/enum/enum-discrim-autosizing.stderr @@ -4,7 +4,7 @@ error[E0081]: discriminant value `0` already exists LL | Au64 = 0, | - first use of `0` LL | Bu64 = 0x8000_0000_0000_0000 - | ^^^^^^^^^^^^^^^^^^^^^ enum already has `0` + | ^^^^^^^^^^^^^^^^^^^^^ enum already has `0` (overflowed from `9223372036854775808`) error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0081.rs b/src/test/ui/error-codes/E0081.rs index 33c8c14306b..255e05ced19 100644 --- a/src/test/ui/error-codes/E0081.rs +++ b/src/test/ui/error-codes/E0081.rs @@ -1,9 +1,20 @@ enum Enum { P = 3, + //~^ NOTE first use of `3` X = 3, //~^ ERROR discriminant value `3` already exists + //~| NOTE enum already has `3` Y = 5 } +#[repr(u8)] +enum EnumOverflowRepr { + P = 257, + //~^ NOTE first use of `1` (overflowed from `257`) + X = 513, + //~^ ERROR discriminant value `1` already exists + //~| NOTE enum already has `1` (overflowed from `513`) +} + fn main() { } diff --git a/src/test/ui/error-codes/E0081.stderr b/src/test/ui/error-codes/E0081.stderr index 0b3d351e1ac..9b279bb0214 100644 --- a/src/test/ui/error-codes/E0081.stderr +++ b/src/test/ui/error-codes/E0081.stderr @@ -1,11 +1,21 @@ error[E0081]: discriminant value `3` already exists - --> $DIR/E0081.rs:3:9 + --> $DIR/E0081.rs:4:9 | LL | P = 3, | - first use of `3` +LL | LL | X = 3, | ^ enum already has `3` -error: aborting due to previous error +error[E0081]: discriminant value `1` already exists + --> $DIR/E0081.rs:14:9 + | +LL | P = 257, + | --- first use of `1` (overflowed from `257`) +LL | +LL | X = 513, + | ^^^ enum already has `1` (overflowed from `513`) + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0081`. diff --git a/src/test/ui/error-codes/E0161.edition.stderr b/src/test/ui/error-codes/E0161.edition.stderr index 536a81a4bc6..6beb29c57d5 100644 --- a/src/test/ui/error-codes/E0161.edition.stderr +++ b/src/test/ui/error-codes/E0161.edition.stderr @@ -1,8 +1,8 @@ -error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined - --> $DIR/E0161.rs:22:9 +error[E0161]: cannot move a value of type dyn Bar: the size of dyn Bar cannot be statically determined + --> $DIR/E0161.rs:29:5 | -LL | box *x; - | ^^ +LL | x.f(); + | ^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0161.editionul.stderr b/src/test/ui/error-codes/E0161.editionul.stderr deleted file mode 100644 index 2baba998f12..00000000000 --- a/src/test/ui/error-codes/E0161.editionul.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined - --> $DIR/E0161.rs:22:5 - | -LL | box *x; - | ^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0161`. diff --git a/src/test/ui/error-codes/E0161.migrate.stderr b/src/test/ui/error-codes/E0161.migrate.stderr index 536a81a4bc6..6beb29c57d5 100644 --- a/src/test/ui/error-codes/E0161.migrate.stderr +++ b/src/test/ui/error-codes/E0161.migrate.stderr @@ -1,8 +1,8 @@ -error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined - --> $DIR/E0161.rs:22:9 +error[E0161]: cannot move a value of type dyn Bar: the size of dyn Bar cannot be statically determined + --> $DIR/E0161.rs:29:5 | -LL | box *x; - | ^^ +LL | x.f(); + | ^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0161.migrateul.stderr b/src/test/ui/error-codes/E0161.migrateul.stderr deleted file mode 100644 index 2baba998f12..00000000000 --- a/src/test/ui/error-codes/E0161.migrateul.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined - --> $DIR/E0161.rs:22:5 - | -LL | box *x; - | ^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0161`. diff --git a/src/test/ui/error-codes/E0161.nll.stderr b/src/test/ui/error-codes/E0161.nll.stderr index 536a81a4bc6..6beb29c57d5 100644 --- a/src/test/ui/error-codes/E0161.nll.stderr +++ b/src/test/ui/error-codes/E0161.nll.stderr @@ -1,8 +1,8 @@ -error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined - --> $DIR/E0161.rs:22:9 +error[E0161]: cannot move a value of type dyn Bar: the size of dyn Bar cannot be statically determined + --> $DIR/E0161.rs:29:5 | -LL | box *x; - | ^^ +LL | x.f(); + | ^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0161.nllul.stderr b/src/test/ui/error-codes/E0161.nllul.stderr deleted file mode 100644 index 2baba998f12..00000000000 --- a/src/test/ui/error-codes/E0161.nllul.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined - --> $DIR/E0161.rs:22:5 - | -LL | box *x; - | ^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0161`. diff --git a/src/test/ui/error-codes/E0161.rs b/src/test/ui/error-codes/E0161.rs index e0f5776424e..ba74529e4b6 100644 --- a/src/test/ui/error-codes/E0161.rs +++ b/src/test/ui/error-codes/E0161.rs @@ -8,6 +8,10 @@ //[edition]edition:2018 //[zflagsul]compile-flags: -Z borrowck=migrate //[editionul]edition:2018 +//[migrateul] check-pass +//[nllul] check-pass +//[zflagsul] check-pass +//[editionul] check-pass #![allow(incomplete_features)] #![cfg_attr(nll, feature(nll))] @@ -16,12 +20,14 @@ #![cfg_attr(zflagsul, feature(unsized_locals))] #![cfg_attr(nllul, feature(unsized_locals))] #![cfg_attr(editionul, feature(unsized_locals))] -#![feature(box_syntax)] -fn foo(x: Box<[i32]>) { - box *x; +trait Bar { + fn f(self); +} + +fn foo(x: Box<dyn Bar>) { + x.f(); //[migrate,nll,zflags,edition]~^ ERROR E0161 - //[migrateul,nllul,zflagsul,editionul]~^^ ERROR E0161 } fn main() {} diff --git a/src/test/ui/error-codes/E0161.zflags.stderr b/src/test/ui/error-codes/E0161.zflags.stderr index 536a81a4bc6..6beb29c57d5 100644 --- a/src/test/ui/error-codes/E0161.zflags.stderr +++ b/src/test/ui/error-codes/E0161.zflags.stderr @@ -1,8 +1,8 @@ -error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined - --> $DIR/E0161.rs:22:9 +error[E0161]: cannot move a value of type dyn Bar: the size of dyn Bar cannot be statically determined + --> $DIR/E0161.rs:29:5 | -LL | box *x; - | ^^ +LL | x.f(); + | ^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0161.zflagsul.stderr b/src/test/ui/error-codes/E0161.zflagsul.stderr deleted file mode 100644 index 2baba998f12..00000000000 --- a/src/test/ui/error-codes/E0161.zflagsul.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined - --> $DIR/E0161.rs:22:5 - | -LL | box *x; - | ^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0161`. diff --git a/src/test/ui/feature-gates/feature-gate-global_asm.rs b/src/test/ui/feature-gates/feature-gate-global_asm.rs index 8c9f22e9752..1420eef299b 100644 --- a/src/test/ui/feature-gates/feature-gate-global_asm.rs +++ b/src/test/ui/feature-gates/feature-gate-global_asm.rs @@ -1,3 +1,5 @@ +// needs-asm-support + global_asm!(""); //~ ERROR `global_asm!` is not stable fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-global_asm.stderr b/src/test/ui/feature-gates/feature-gate-global_asm.stderr index e07fbf00d57..7c4d3e3e6e5 100644 --- a/src/test/ui/feature-gates/feature-gate-global_asm.stderr +++ b/src/test/ui/feature-gates/feature-gate-global_asm.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature 'global_asm': `global_asm!` is not stable enough for use and is subject to change - --> $DIR/feature-gate-global_asm.rs:1:1 + --> $DIR/feature-gate-global_asm.rs:3:1 | LL | global_asm!(""); | ^^^^^^^^^^ diff --git a/src/test/ui/lint/force-warn/force-lint-allow-all-warnings.rs b/src/test/ui/lint/force-warn/allow-warnings.rs index 0e8a65a4117..6ee5ba67932 100644 --- a/src/test/ui/lint/force-warn/force-lint-allow-all-warnings.rs +++ b/src/test/ui/lint/force-warn/allow-warnings.rs @@ -1,3 +1,5 @@ +// --force-warn $LINT causes $LINT (which is warn-by-default) to warn +// despite allowing all warnings in module // compile-flags: --force-warn dead_code -Zunstable-options // check-pass diff --git a/src/test/ui/lint/force-warn/force-allowed-warning.stderr b/src/test/ui/lint/force-warn/allow-warnings.stderr index fced147254e..cac2b4e9189 100644 --- a/src/test/ui/lint/force-warn/force-allowed-warning.stderr +++ b/src/test/ui/lint/force-warn/allow-warnings.stderr @@ -1,5 +1,5 @@ warning: function is never used: `dead_function` - --> $DIR/force-allowed-warning.rs:6:4 + --> $DIR/allow-warnings.rs:8:4 | LL | fn dead_function() {} | ^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/force-warn/force-allowed-by-default-lint.rs b/src/test/ui/lint/force-warn/allowed-by-default-lint.rs index 4799429ea2c..fd0b886d84d 100644 --- a/src/test/ui/lint/force-warn/force-allowed-by-default-lint.rs +++ b/src/test/ui/lint/force-warn/allowed-by-default-lint.rs @@ -1,3 +1,4 @@ +// --force-warn $LINT causes $LINT (which is allow-by-default) to warn // compile-flags: --force-warn elided_lifetimes_in_paths -Zunstable-options // check-pass diff --git a/src/test/ui/lint/force-warn/force-allowed-by-default-lint.stderr b/src/test/ui/lint/force-warn/allowed-by-default-lint.stderr index 05513de81d1..baa47cbb10f 100644 --- a/src/test/ui/lint/force-warn/force-allowed-by-default-lint.stderr +++ b/src/test/ui/lint/force-warn/allowed-by-default-lint.stderr @@ -1,5 +1,5 @@ warning: hidden lifetime parameters in types are deprecated - --> $DIR/force-allowed-by-default-lint.rs:8:12 + --> $DIR/allowed-by-default-lint.rs:9:12 | LL | fn foo(x: &Foo) {} | ^^^- help: indicate the anonymous lifetime: `<'_>` diff --git a/src/test/ui/lint/force-warn/allowed-cli-deny-by-default-lint.rs b/src/test/ui/lint/force-warn/allowed-cli-deny-by-default-lint.rs new file mode 100644 index 00000000000..3fab9148392 --- /dev/null +++ b/src/test/ui/lint/force-warn/allowed-cli-deny-by-default-lint.rs @@ -0,0 +1,10 @@ +// --force-warn $LINT causes $LINT (which is deny-by-default) to warn +// despite $LINT being allowed on command line +// compile-flags: -A const_err --force-warn const_err -Zunstable-options +// check-pass + +const C: i32 = 1 / 0; +//~^ WARN any use of this value will cause an error +//~| WARN this was previously accepted by the compiler + +fn main() {} diff --git a/src/test/ui/lint/force-warn/force-allowed-deny-by-default-lint.stderr b/src/test/ui/lint/force-warn/allowed-cli-deny-by-default-lint.stderr index dd4f88a3b53..af6308f0d1b 100644 --- a/src/test/ui/lint/force-warn/force-allowed-deny-by-default-lint.stderr +++ b/src/test/ui/lint/force-warn/allowed-cli-deny-by-default-lint.stderr @@ -1,5 +1,5 @@ warning: any use of this value will cause an error - --> $DIR/force-allowed-deny-by-default-lint.rs:5:16 + --> $DIR/allowed-cli-deny-by-default-lint.rs:6:16 | LL | const C: i32 = 1 / 0; | ---------------^^^^^- diff --git a/src/test/ui/lint/force-warn/force-deny-by-default-lint.rs b/src/test/ui/lint/force-warn/allowed-deny-by-default-lint.rs index 8331df02da7..82a584ac972 100644 --- a/src/test/ui/lint/force-warn/force-deny-by-default-lint.rs +++ b/src/test/ui/lint/force-warn/allowed-deny-by-default-lint.rs @@ -1,6 +1,9 @@ +// --force-warn $LINT causes $LINT (which is deny-by-default) to warn +// despite $LINT being allowed in module // compile-flags: --force-warn const_err -Zunstable-options // check-pass +#![allow(const_err)] const C: i32 = 1 / 0; //~^ WARN any use of this value will cause an error //~| WARN this was previously accepted by the compiler diff --git a/src/test/ui/lint/force-warn/allowed-deny-by-default-lint.stderr b/src/test/ui/lint/force-warn/allowed-deny-by-default-lint.stderr new file mode 100644 index 00000000000..05656afd22d --- /dev/null +++ b/src/test/ui/lint/force-warn/allowed-deny-by-default-lint.stderr @@ -0,0 +1,14 @@ +warning: any use of this value will cause an error + --> $DIR/allowed-deny-by-default-lint.rs:7:16 + | +LL | const C: i32 = 1 / 0; + | ---------------^^^^^- + | | + | attempt to divide `1_i32` by zero + | + = note: requested on the command line with `--force-warn const-err` + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +warning: 1 warning emitted + diff --git a/src/test/ui/lint/force-warn/force-lint-in-allowed-group.rs b/src/test/ui/lint/force-warn/allowed-group-warn-by-default-lint.rs index d8447bd2382..86ab12668a3 100644 --- a/src/test/ui/lint/force-warn/force-lint-in-allowed-group.rs +++ b/src/test/ui/lint/force-warn/allowed-group-warn-by-default-lint.rs @@ -1,3 +1,5 @@ +// --force-warn $LINT causes $LINT (which is warn-by-default) to warn +// despite $LINT_GROUP (which contains $LINT) being allowed // compile-flags: --force-warn bare_trait_objects -Zunstable-options // check-pass diff --git a/src/test/ui/lint/force-warn/force-lint-in-allowed-group.stderr b/src/test/ui/lint/force-warn/allowed-group-warn-by-default-lint.stderr index c1ebdb9514b..d945cc3347a 100644 --- a/src/test/ui/lint/force-warn/force-lint-in-allowed-group.stderr +++ b/src/test/ui/lint/force-warn/allowed-group-warn-by-default-lint.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/force-lint-in-allowed-group.rs:8:25 + --> $DIR/allowed-group-warn-by-default-lint.rs:10:25 | LL | pub fn function(_x: Box<SomeTrait>) {} | ^^^^^^^^^ help: use `dyn`: `dyn SomeTrait` diff --git a/src/test/ui/lint/force-warn/force-allowed-warning.rs b/src/test/ui/lint/force-warn/allowed-warn-by-default-lint.rs index 280de506472..7204782a324 100644 --- a/src/test/ui/lint/force-warn/force-allowed-warning.rs +++ b/src/test/ui/lint/force-warn/allowed-warn-by-default-lint.rs @@ -1,3 +1,5 @@ +// --force-warn $LINT causes $LINT (which is warn-by-default) to warn +// despite $LINT being allowed in module // compile-flags: --force-warn dead_code -Zunstable-options // check-pass diff --git a/src/test/ui/lint/force-warn/force-lint-allow-all-warnings.stderr b/src/test/ui/lint/force-warn/allowed-warn-by-default-lint.stderr index 3305f2c0283..c46d7403fd0 100644 --- a/src/test/ui/lint/force-warn/force-lint-allow-all-warnings.stderr +++ b/src/test/ui/lint/force-warn/allowed-warn-by-default-lint.stderr @@ -1,5 +1,5 @@ warning: function is never used: `dead_function` - --> $DIR/force-lint-allow-all-warnings.rs:6:4 + --> $DIR/allowed-warn-by-default-lint.rs:8:4 | LL | fn dead_function() {} | ^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/force-warn/force-warn-cap-lints-allow.rs b/src/test/ui/lint/force-warn/cap-lints-allow.rs index e10d161e7c6..de3a1bd8dd7 100644 --- a/src/test/ui/lint/force-warn/force-warn-cap-lints-allow.rs +++ b/src/test/ui/lint/force-warn/cap-lints-allow.rs @@ -1,3 +1,5 @@ +// --force-warn $LINT casuses $LINT to warn despite --cap-lints +// set to allow // compile-flags: --cap-lints allow --force-warn bare_trait_objects -Zunstable-options // check-pass diff --git a/src/test/ui/lint/force-warn/force-warn-cap-lints-allow.stderr b/src/test/ui/lint/force-warn/cap-lints-allow.stderr index 8514956af74..f3ae16b5657 100644 --- a/src/test/ui/lint/force-warn/force-warn-cap-lints-allow.stderr +++ b/src/test/ui/lint/force-warn/cap-lints-allow.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/force-warn-cap-lints-allow.rs:6:25 + --> $DIR/cap-lints-allow.rs:8:25 | LL | pub fn function(_x: Box<SomeTrait>) {} | ^^^^^^^^^ help: use `dyn`: `dyn SomeTrait` diff --git a/src/test/ui/lint/force-warn/force-warn-cap-lints-warn.rs b/src/test/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.rs index 4afc0868608..70fb90dc199 100644 --- a/src/test/ui/lint/force-warn/force-warn-cap-lints-warn.rs +++ b/src/test/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.rs @@ -1,3 +1,5 @@ +// --force-warn $LINT_GROUP causes $LINT to warn despite $LINT being +// allowed in module and cap-lints set to warn // compile-flags: --cap-lints warn --force-warn rust-2021-compatibility -Zunstable-options // check-pass #![allow(ellipsis_inclusive_range_patterns)] diff --git a/src/test/ui/lint/force-warn/force-warn-cap-lints-warn.stderr b/src/test/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr index 3a0227463e6..3dafaf7055f 100644 --- a/src/test/ui/lint/force-warn/force-warn-cap-lints-warn.stderr +++ b/src/test/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr @@ -1,5 +1,5 @@ warning: `...` range patterns are deprecated - --> $DIR/force-warn-cap-lints-warn.rs:8:10 + --> $DIR/cap-lints-warn-allowed-warn-by-default-lint.rs:10:10 | LL | 0...100 => true, | ^^^ help: use `..=` for an inclusive range diff --git a/src/test/ui/lint/force-warn/force-allowed-deny-by-default-lint.rs b/src/test/ui/lint/force-warn/deny-by-default-lint.rs index d066feba869..b0a15cc2fba 100644 --- a/src/test/ui/lint/force-warn/force-allowed-deny-by-default-lint.rs +++ b/src/test/ui/lint/force-warn/deny-by-default-lint.rs @@ -1,7 +1,7 @@ +// --force-warn $LINT causes $LINT (which is deny-by-default) to warn // compile-flags: --force-warn const_err -Zunstable-options // check-pass -#![allow(const_err)] const C: i32 = 1 / 0; //~^ WARN any use of this value will cause an error //~| WARN this was previously accepted by the compiler diff --git a/src/test/ui/lint/force-warn/force-deny-by-default-lint.stderr b/src/test/ui/lint/force-warn/deny-by-default-lint.stderr index 68cd3a392f5..ef295f99e64 100644 --- a/src/test/ui/lint/force-warn/force-deny-by-default-lint.stderr +++ b/src/test/ui/lint/force-warn/deny-by-default-lint.stderr @@ -1,5 +1,5 @@ warning: any use of this value will cause an error - --> $DIR/force-deny-by-default-lint.rs:4:16 + --> $DIR/deny-by-default-lint.rs:5:16 | LL | const C: i32 = 1 / 0; | ---------------^^^^^- diff --git a/src/test/ui/lint/force-warn/force-lint-group-allow-all-warnings.rs b/src/test/ui/lint/force-warn/lint-group-allow-warnings.rs index aaca59a2a2a..e5dcd9a7ea1 100644 --- a/src/test/ui/lint/force-warn/force-lint-group-allow-all-warnings.rs +++ b/src/test/ui/lint/force-warn/lint-group-allow-warnings.rs @@ -1,3 +1,6 @@ +// --force-warn $LINT_GROUP causes $LINT in $LINT_GROUP to warn +// despite all warnings being allowed in module +// warn-by-default lint to warn // compile-flags: --force-warn nonstandard_style -Zunstable-options // check-pass diff --git a/src/test/ui/lint/force-warn/force-lint-group-allow-all-warnings.stderr b/src/test/ui/lint/force-warn/lint-group-allow-warnings.stderr index 065a8f6a556..dc7b1b7b98d 100644 --- a/src/test/ui/lint/force-warn/force-lint-group-allow-all-warnings.stderr +++ b/src/test/ui/lint/force-warn/lint-group-allow-warnings.stderr @@ -1,5 +1,5 @@ warning: function `FUNCTION` should have a snake case name - --> $DIR/force-lint-group-allow-all-warnings.rs:6:8 + --> $DIR/lint-group-allow-warnings.rs:9:8 | LL | pub fn FUNCTION() {} | ^^^^^^^^ help: convert the identifier to snake case: `function` diff --git a/src/test/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.rs b/src/test/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.rs new file mode 100644 index 00000000000..4eb05b538b0 --- /dev/null +++ b/src/test/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.rs @@ -0,0 +1,12 @@ +// --force-warn $LINT_GROUP causes $LINT (which is warn-by-default) to warn +// despite $LINT being allowed on command line +// compile-flags: -A bare-trait-objects --force-warn rust-2018-idioms -Zunstable-options +// check-pass + +pub trait SomeTrait {} + +pub fn function(_x: Box<SomeTrait>) {} +//~^ WARN trait objects without an explicit `dyn` are deprecated +//~| WARN this is accepted in the current edition + +fn main() {} diff --git a/src/test/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr b/src/test/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr new file mode 100644 index 00000000000..dc62521bf89 --- /dev/null +++ b/src/test/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr @@ -0,0 +1,12 @@ +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/lint-group-allowed-cli-warn-by-default-lint.rs:8:25 + | +LL | pub fn function(_x: Box<SomeTrait>) {} + | ^^^^^^^^^ help: use `dyn`: `dyn SomeTrait` + | + = note: `--force-warn bare-trait-objects` implied by `--force-warn rust-2018-idioms` + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> + +warning: 1 warning emitted + diff --git a/src/test/ui/lint/force-warn/force-warn-group.rs b/src/test/ui/lint/force-warn/lint-group-allowed-lint-group.rs index 0198610b78e..dc13b2b2474 100644 --- a/src/test/ui/lint/force-warn/force-warn-group.rs +++ b/src/test/ui/lint/force-warn/lint-group-allowed-lint-group.rs @@ -1,3 +1,5 @@ +// --force-warn $LINT_GROUP causes $LINT to warn despite +// $LINT_GROUP being allowed in module // compile-flags: --force-warn rust_2018_idioms -Zunstable-options // check-pass diff --git a/src/test/ui/lint/force-warn/force-warn-group.stderr b/src/test/ui/lint/force-warn/lint-group-allowed-lint-group.stderr index 54bee452cdd..fcbae024eb6 100644 --- a/src/test/ui/lint/force-warn/force-warn-group.stderr +++ b/src/test/ui/lint/force-warn/lint-group-allowed-lint-group.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/force-warn-group.rs:8:25 + --> $DIR/lint-group-allowed-lint-group.rs:10:25 | LL | pub fn function(_x: Box<SomeTrait>) {} | ^^^^^^^^^ help: use `dyn`: `dyn SomeTrait` diff --git a/src/test/ui/lint/force-warn/force-warn-group-allow-warning.rs b/src/test/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.rs index 193ba2b6f0d..b7f79b3d4aa 100644 --- a/src/test/ui/lint/force-warn/force-warn-group-allow-warning.rs +++ b/src/test/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.rs @@ -1,3 +1,5 @@ +// --force-warn $LINT_GROUP causes $LINT (which is warn-by-default) to warn +// despite $LINT being allowed in module // compile-flags: --force-warn rust-2018-idioms -Zunstable-options // check-pass diff --git a/src/test/ui/lint/force-warn/force-warn-group-allow-warning.stderr b/src/test/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr index 29eba6d635f..1212ae083c2 100644 --- a/src/test/ui/lint/force-warn/force-warn-group-allow-warning.stderr +++ b/src/test/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/force-warn-group-allow-warning.rs:8:25 + --> $DIR/lint-group-allowed-warn-by-default-lint.rs:10:25 | LL | pub fn function(_x: Box<SomeTrait>) {} | ^^^^^^^^^ help: use `dyn`: `dyn SomeTrait` diff --git a/src/test/ui/lint/force-warn/warn-by-default-lint-two-modules.rs b/src/test/ui/lint/force-warn/warn-by-default-lint-two-modules.rs new file mode 100644 index 00000000000..d2cb3417be6 --- /dev/null +++ b/src/test/ui/lint/force-warn/warn-by-default-lint-two-modules.rs @@ -0,0 +1,18 @@ +// --force-warn $LINT causes $LINT (which is warn-by-default) to warn +// despite being allowed in one submodule (but not the other) +// compile-flags: --force-warn dead_code -Zunstable-options +// check-pass + +mod one { + #![allow(dead_code)] + + fn dead_function() {} + //~^ WARN function is never used +} + +mod two { + fn dead_function() {} + //~^ WARN function is never used +} + +fn main() {} diff --git a/src/test/ui/lint/force-warn/warn-by-default-lint-two-modules.stderr b/src/test/ui/lint/force-warn/warn-by-default-lint-two-modules.stderr new file mode 100644 index 00000000000..2a3cf85a1e3 --- /dev/null +++ b/src/test/ui/lint/force-warn/warn-by-default-lint-two-modules.stderr @@ -0,0 +1,16 @@ +warning: function is never used: `dead_function` + --> $DIR/warn-by-default-lint-two-modules.rs:9:8 + | +LL | fn dead_function() {} + | ^^^^^^^^^^^^^ + | + = note: requested on the command line with `--force-warn dead-code` + +warning: function is never used: `dead_function` + --> $DIR/warn-by-default-lint-two-modules.rs:14:8 + | +LL | fn dead_function() {} + | ^^^^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/src/test/ui/lint/force-warn/warnings-lint-group.rs b/src/test/ui/lint/force-warn/warnings-lint-group.rs new file mode 100644 index 00000000000..fa25a1f8a84 --- /dev/null +++ b/src/test/ui/lint/force-warn/warnings-lint-group.rs @@ -0,0 +1,5 @@ +// --force-warn warnings is an error +// compile-flags: --force-warn warnings -Zunstable-options +// error-pattern: `warnings` lint group is not supported + +fn main() {} diff --git a/src/test/ui/lint/force-warn/warnings-lint-group.stderr b/src/test/ui/lint/force-warn/warnings-lint-group.stderr new file mode 100644 index 00000000000..03f5788b527 --- /dev/null +++ b/src/test/ui/lint/force-warn/warnings-lint-group.stderr @@ -0,0 +1,9 @@ +error[E0602]: `warnings` lint group is not supported with ´--force-warn´ + +error[E0602]: `warnings` lint group is not supported with ´--force-warn´ + +error[E0602]: `warnings` lint group is not supported with ´--force-warn´ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0602`. diff --git a/src/test/ui/llvm-asm/issue-69092.rs b/src/test/ui/llvm-asm/issue-69092.rs index 26fccec80f1..ea1b80cc9b0 100644 --- a/src/test/ui/llvm-asm/issue-69092.rs +++ b/src/test/ui/llvm-asm/issue-69092.rs @@ -1,5 +1,7 @@ // build-fail // ignore-emscripten no asm! support +// The error message differs slightly between LLVM versions +// min-llvm-version: 13.0 // Regression test for #69092 #![feature(llvm_asm)] @@ -7,5 +9,5 @@ fn main() { unsafe { llvm_asm!(".ascii \"Xen\0\""); } - //~^ ERROR: expected string in '.ascii' directive + //~^ ERROR: expected string } diff --git a/src/test/ui/llvm-asm/issue-69092.stderr b/src/test/ui/llvm-asm/issue-69092.stderr index 15a35e43c27..28c5fbbca3c 100644 --- a/src/test/ui/llvm-asm/issue-69092.stderr +++ b/src/test/ui/llvm-asm/issue-69092.stderr @@ -1,5 +1,5 @@ -error: expected string in '.ascii' directive - --> $DIR/issue-69092.rs:9:14 +error: expected string + --> $DIR/issue-69092.rs:11:14 | LL | unsafe { llvm_asm!(".ascii \"Xen\0\""); } | ^ diff --git a/src/test/ui/macros/issue-88228.rs b/src/test/ui/macros/issue-88228.rs new file mode 100644 index 00000000000..615b865e9f1 --- /dev/null +++ b/src/test/ui/macros/issue-88228.rs @@ -0,0 +1,22 @@ +// compile-flags: -Z deduplicate-diagnostics=yes +// edition:2018 + +mod hey { + pub use Copy as Bla; + pub use std::println as bla; +} + +#[derive(Bla)] +//~^ ERROR cannot find derive macro `Bla` +//~| NOTE consider importing this derive macro +struct A; + +#[derive(println)] +//~^ ERROR cannot find derive macro `println` +struct B; + +fn main() { + bla!(); + //~^ ERROR cannot find macro `bla` + //~| NOTE consider importing this macro +} diff --git a/src/test/ui/macros/issue-88228.stderr b/src/test/ui/macros/issue-88228.stderr new file mode 100644 index 00000000000..b164e39064c --- /dev/null +++ b/src/test/ui/macros/issue-88228.stderr @@ -0,0 +1,26 @@ +error: cannot find macro `bla` in this scope + --> $DIR/issue-88228.rs:19:5 + | +LL | bla!(); + | ^^^ + | + = note: consider importing this macro: + crate::hey::bla + +error: cannot find derive macro `println` in this scope + --> $DIR/issue-88228.rs:14:10 + | +LL | #[derive(println)] + | ^^^^^^^ + +error: cannot find derive macro `Bla` in this scope + --> $DIR/issue-88228.rs:9:10 + | +LL | #[derive(Bla)] + | ^^^ + | + = note: consider importing this derive macro: + crate::hey::Bla + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/macros/macro-expanded-include/test.rs b/src/test/ui/macros/macro-expanded-include/test.rs index f1a71059a89..6a2b5ef7241 100644 --- a/src/test/ui/macros/macro-expanded-include/test.rs +++ b/src/test/ui/macros/macro-expanded-include/test.rs @@ -1,4 +1,4 @@ -// ignore-emscripten no llvm_asm! support +// needs-asm-support // build-pass (FIXME(62277): could be check-pass?) #![feature(asm)] #![allow(unused)] diff --git a/src/test/ui/marker_trait_attr/overlapping-impl-1-modulo-regions.rs b/src/test/ui/marker_trait_attr/overlapping-impl-1-modulo-regions.rs new file mode 100644 index 00000000000..a8f3db5f5b2 --- /dev/null +++ b/src/test/ui/marker_trait_attr/overlapping-impl-1-modulo-regions.rs @@ -0,0 +1,9 @@ +// check-pass +#![feature(marker_trait_attr)] + +#[marker] +pub trait F {} +impl<T> F for T where T: Copy {} +impl<T> F for T where T: 'static {} + +fn main() {} diff --git a/src/test/ui/marker_trait_attr/region-overlap.rs b/src/test/ui/marker_trait_attr/region-overlap.rs new file mode 100644 index 00000000000..b3c66710355 --- /dev/null +++ b/src/test/ui/marker_trait_attr/region-overlap.rs @@ -0,0 +1,8 @@ +#![feature(marker_trait_attr)] + +#[marker] +trait A {} +impl<'a> A for (&'static (), &'a ()) {} //~ ERROR type annotations needed +impl<'a> A for (&'a (), &'static ()) {} //~ ERROR type annotations needed + +fn main() {} diff --git a/src/test/ui/marker_trait_attr/region-overlap.stderr b/src/test/ui/marker_trait_attr/region-overlap.stderr new file mode 100644 index 00000000000..e4a94d56f12 --- /dev/null +++ b/src/test/ui/marker_trait_attr/region-overlap.stderr @@ -0,0 +1,29 @@ +error[E0283]: type annotations needed + --> $DIR/region-overlap.rs:5:10 + | +LL | impl<'a> A for (&'static (), &'a ()) {} + | ^ cannot infer type for tuple `(&'static (), &'a ())` + | + = note: cannot satisfy `(&'static (), &'a ()): A` +note: required by a bound in `A` + --> $DIR/region-overlap.rs:4:1 + | +LL | trait A {} + | ^^^^^^^ required by this bound in `A` + +error[E0283]: type annotations needed + --> $DIR/region-overlap.rs:6:10 + | +LL | impl<'a> A for (&'a (), &'static ()) {} + | ^ cannot infer type for tuple `(&'a (), &'static ())` + | + = note: cannot satisfy `(&'a (), &'static ()): A` +note: required by a bound in `A` + --> $DIR/region-overlap.rs:4:1 + | +LL | trait A {} + | ^^^^^^^ required by this bound in `A` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0283`. diff --git a/src/test/ui/marker_trait_attr/unsound-overlap.rs b/src/test/ui/marker_trait_attr/unsound-overlap.rs new file mode 100644 index 00000000000..2e5101b822c --- /dev/null +++ b/src/test/ui/marker_trait_attr/unsound-overlap.rs @@ -0,0 +1,25 @@ +#![feature(marker_trait_attr)] + +#[marker] +trait A {} + +trait B {} + +impl<T: A> B for T {} +impl<T: B> A for T {} +impl A for &str {} +impl<T: A + B> A for (T,) {} +trait TraitWithAssoc { + type Assoc; +} + +impl<T: A> TraitWithAssoc for T { + type Assoc = T; +} + +impl TraitWithAssoc for ((&str,),) { + //~^ ERROR conflicting implementations + type Assoc = ((&'static str,),); +} + +fn main() {} diff --git a/src/test/ui/marker_trait_attr/unsound-overlap.stderr b/src/test/ui/marker_trait_attr/unsound-overlap.stderr new file mode 100644 index 00000000000..5ebac8270dd --- /dev/null +++ b/src/test/ui/marker_trait_attr/unsound-overlap.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `TraitWithAssoc` for type `((&str,),)` + --> $DIR/unsound-overlap.rs:20:1 + | +LL | impl<T: A> TraitWithAssoc for T { + | ------------------------------- first implementation here +... +LL | impl TraitWithAssoc for ((&str,),) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `((&str,),)` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/proc-macro/derive-helper-shadowing.stderr b/src/test/ui/proc-macro/derive-helper-shadowing.stderr index 4115fec86fb..3b160935a2f 100644 --- a/src/test/ui/proc-macro/derive-helper-shadowing.stderr +++ b/src/test/ui/proc-macro/derive-helper-shadowing.stderr @@ -16,6 +16,8 @@ error: cannot find attribute `empty_helper` in this scope LL | #[derive(GenHelperUse)] | ^^^^^^^^^^^^ | + = note: consider importing this attribute macro: + empty_helper = note: this error originates in the derive macro `GenHelperUse` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find attribute `empty_helper` in this scope @@ -27,6 +29,8 @@ LL | #[empty_helper] LL | gen_helper_use!(); | ------------------ in this macro invocation | + = note: consider importing this attribute macro: + crate::empty_helper = note: this error originates in the macro `gen_helper_use` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0659]: `empty_helper` is ambiguous (name vs any other name during import resolution) diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr index 7f197a238e5..be55890c08c 100644 --- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr +++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr @@ -1,7 +1,7 @@ -error[E0391]: cycle detected when computing layout of `S` +error[E0391]: cycle detected when computing layout of `std::option::Option<S>` | - = note: ...which requires computing layout of `std::option::Option<S>`... - = note: ...which again requires computing layout of `S`, completing the cycle + = note: ...which requires computing layout of `S`... + = note: ...which again requires computing layout of `std::option::Option<S>`, completing the cycle note: cycle used when optimizing MIR for `main` --> $DIR/issue-26548-recursion-via-normalize.rs:15:1 | diff --git a/src/test/ui/traits/trait-upcasting/correct-supertrait-substitution.rs b/src/test/ui/traits/trait-upcasting/correct-supertrait-substitution.rs new file mode 100644 index 00000000000..8d0a9ef0ace --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/correct-supertrait-substitution.rs @@ -0,0 +1,39 @@ +// run-pass +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +trait Foo<T: Default + ToString>: Bar<i32> + Bar<T> {} +trait Bar<T: Default + ToString> { + fn bar(&self) -> String { + T::default().to_string() + } +} + +struct S1; + +impl Bar<i32> for S1 {} +impl Foo<i32> for S1 {} + +struct S2; +impl Bar<i32> for S2 {} +impl Bar<bool> for S2 {} +impl Foo<bool> for S2 {} + +fn test1(x: &dyn Foo<i32>) { + let s = x as &dyn Bar<i32>; + assert_eq!("0", &s.bar().to_string()); +} + +fn test2(x: &dyn Foo<bool>) { + let p = x as &dyn Bar<i32>; + assert_eq!("0", &p.bar().to_string()); + let q = x as &dyn Bar<bool>; + assert_eq!("false", &q.bar().to_string()); +} + +fn main() { + let s1 = S1; + test1(&s1); + let s2 = S2; + test2(&s2); +} diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs index 1a0e5072843..79ddedd4187 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs @@ -9,12 +9,8 @@ trait Bar<T> { } fn test_specific(x: &dyn Foo) { - let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar<i32>` is not satisfied - let _ = x as &dyn Bar<u32>; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar<u32>` is not satisfied + let _ = x as &dyn Bar<i32>; // OK + let _ = x as &dyn Bar<u32>; // OK } fn test_unknown_version(x: &dyn Foo) { @@ -24,9 +20,7 @@ fn test_unknown_version(x: &dyn Foo) { } fn test_infer_version(x: &dyn Foo) { - let a = x as &dyn Bar<_>; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo: Bar<u32>` is not satisfied + let a = x as &dyn Bar<_>; // OK let _: Option<u32> = a.bar(); } diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr index da647f16c3d..44f32e0cec9 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr @@ -1,43 +1,5 @@ -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<i32>` - --> $DIR/type-checking-test-1.rs:12:13 - | -LL | let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar<i32>; // FIXME: OK, eventually - | + - -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<u32>` - --> $DIR/type-checking-test-1.rs:15:13 - | -LL | let _ = x as &dyn Bar<u32>; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar<u32>; // FIXME: OK, eventually - | + - -error[E0277]: the trait bound `&dyn Foo: Bar<i32>` is not satisfied - --> $DIR/type-checking-test-1.rs:12:13 - | -LL | let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually - | ^ the trait `Bar<i32>` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar<i32>` - -error[E0277]: the trait bound `&dyn Foo: Bar<u32>` is not satisfied - --> $DIR/type-checking-test-1.rs:15:13 - | -LL | let _ = x as &dyn Bar<u32>; // FIXME: OK, eventually - | ^ the trait `Bar<u32>` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar<u32>` - error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` - --> $DIR/type-checking-test-1.rs:21:13 + --> $DIR/type-checking-test-1.rs:17:13 | LL | let _ = x as &dyn Bar<_>; // Ambiguous | ^^^^^^^^^^^^^^^^ invalid cast @@ -48,33 +10,14 @@ LL | let _ = &x as &dyn Bar<_>; // Ambiguous | + error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied - --> $DIR/type-checking-test-1.rs:21:13 + --> $DIR/type-checking-test-1.rs:17:13 | LL | let _ = x as &dyn Bar<_>; // Ambiguous | ^ the trait `Bar<_>` is not implemented for `&dyn Foo` | = note: required for the cast to the object type `dyn Bar<_>` -error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<u32>` - --> $DIR/type-checking-test-1.rs:27:13 - | -LL | let a = x as &dyn Bar<_>; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let a = &x as &dyn Bar<_>; // FIXME: OK, eventually - | + - -error[E0277]: the trait bound `&dyn Foo: Bar<u32>` is not satisfied - --> $DIR/type-checking-test-1.rs:27:13 - | -LL | let a = x as &dyn Bar<_>; // FIXME: OK, eventually - | ^ the trait `Bar<u32>` is not implemented for `&dyn Foo` - | - = note: required for the cast to the object type `dyn Bar<u32>` - -error: aborting due to 8 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0605. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs index 326df74211e..32754c53803 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs @@ -13,9 +13,7 @@ fn test_specific(x: &dyn Foo<i32>) { } fn test_specific2(x: &dyn Foo<u32>) { - let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually - //~^ ERROR non-primitive cast - //~^^ ERROR the trait bound `&dyn Foo<u32>: Bar<i32>` is not satisfied + let _ = x as &dyn Bar<i32>; // OK } fn test_specific3(x: &dyn Foo<i32>) { diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr index 582eddc7aa0..4ae4c8552c1 100644 --- a/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr +++ b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr @@ -1,24 +1,5 @@ -error[E0605]: non-primitive cast: `&dyn Foo<u32>` as `&dyn Bar<i32>` - --> $DIR/type-checking-test-2.rs:16:13 - | -LL | let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually - | ^^^^^^^^^^^^^^^^^^ invalid cast - | -help: consider borrowing the value - | -LL | let _ = &x as &dyn Bar<i32>; // FIXME: OK, eventually - | + - -error[E0277]: the trait bound `&dyn Foo<u32>: Bar<i32>` is not satisfied - --> $DIR/type-checking-test-2.rs:16:13 - | -LL | let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually - | ^ the trait `Bar<i32>` is not implemented for `&dyn Foo<u32>` - | - = note: required for the cast to the object type `dyn Bar<i32>` - error[E0605]: non-primitive cast: `&dyn Foo<i32>` as `&dyn Bar<u32>` - --> $DIR/type-checking-test-2.rs:22:13 + --> $DIR/type-checking-test-2.rs:20:13 | LL | let _ = x as &dyn Bar<u32>; // Error | ^^^^^^^^^^^^^^^^^^ invalid cast @@ -29,7 +10,7 @@ LL | let _ = &x as &dyn Bar<u32>; // Error | + error[E0277]: the trait bound `&dyn Foo<i32>: Bar<u32>` is not satisfied - --> $DIR/type-checking-test-2.rs:22:13 + --> $DIR/type-checking-test-2.rs:20:13 | LL | let _ = x as &dyn Bar<u32>; // Error | ^ the trait `Bar<u32>` is not implemented for `&dyn Foo<i32>` @@ -37,7 +18,7 @@ LL | let _ = x as &dyn Bar<u32>; // Error = note: required for the cast to the object type `dyn Bar<u32>` error[E0605]: non-primitive cast: `&dyn Foo<u32>` as `&dyn Bar<_>` - --> $DIR/type-checking-test-2.rs:28:13 + --> $DIR/type-checking-test-2.rs:26:13 | LL | let a = x as &dyn Bar<_>; // Ambiguous | ^^^^^^^^^^^^^^^^ invalid cast @@ -48,14 +29,14 @@ LL | let a = &x as &dyn Bar<_>; // Ambiguous | + error[E0277]: the trait bound `&dyn Foo<u32>: Bar<_>` is not satisfied - --> $DIR/type-checking-test-2.rs:28:13 + --> $DIR/type-checking-test-2.rs:26:13 | LL | let a = x as &dyn Bar<_>; // Ambiguous | ^ the trait `Bar<_>` is not implemented for `&dyn Foo<u32>` | = note: required for the cast to the object type `dyn Bar<_>` -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0605. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/defining-use-submodule.rs b/src/test/ui/type-alias-impl-trait/defining-use-submodule.rs new file mode 100644 index 00000000000..8b51f55715e --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/defining-use-submodule.rs @@ -0,0 +1,23 @@ +// check-pass + +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +// test that the type alias impl trait defining use is in a submodule + +fn main() {} + +type Foo = impl std::fmt::Display; +type Bar = impl std::fmt::Display; + +mod foo { + pub fn foo() -> super::Foo { + "foo" + } + + pub mod bar { + pub fn bar() -> crate::Bar { + 1 + } + } +} diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained.rs b/src/test/ui/type-alias-impl-trait/generic_underconstrained.rs index 766ee36c02b..d87a25aad58 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained.rs +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained.rs @@ -3,8 +3,7 @@ fn main() {} trait Trait {} -type Underconstrained<T: Trait> = impl 'static; -//~^ ERROR: at least one trait must be specified +type Underconstrained<T: Trait> = impl Send; // no `Trait` bound fn underconstrain<T>(_: T) -> Underconstrained<T> { diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr index 99eb884a0e8..c73288329b0 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr @@ -1,11 +1,5 @@ -error: at least one trait must be specified - --> $DIR/generic_underconstrained.rs:6:35 - | -LL | type Underconstrained<T: Trait> = impl 'static; - | ^^^^^^^^^^^^ - error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/generic_underconstrained.rs:10:31 + --> $DIR/generic_underconstrained.rs:9:31 | LL | fn underconstrain<T>(_: T) -> Underconstrained<T> { | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` @@ -13,13 +7,13 @@ LL | fn underconstrain<T>(_: T) -> Underconstrained<T> { note: required by a bound in `Underconstrained` --> $DIR/generic_underconstrained.rs:6:26 | -LL | type Underconstrained<T: Trait> = impl 'static; +LL | type Underconstrained<T: Trait> = impl Send; | ^^^^^ required by this bound in `Underconstrained` help: consider restricting type parameter `T` | LL | fn underconstrain<T: Trait>(_: T) -> Underconstrained<T> { | +++++++ -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs index cd7c962e2d1..8adc0bf32a6 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs @@ -2,8 +2,7 @@ fn main() {} -type Underconstrained<T: std::fmt::Debug> = impl 'static; -//~^ ERROR: at least one trait must be specified +type Underconstrained<T: std::fmt::Debug> = impl Send; // not a defining use, because it doesn't define *all* possible generics fn underconstrained<U>(_: U) -> Underconstrained<U> { @@ -11,8 +10,7 @@ fn underconstrained<U>(_: U) -> Underconstrained<U> { 5u32 } -type Underconstrained2<T: std::fmt::Debug> = impl 'static; -//~^ ERROR: at least one trait must be specified +type Underconstrained2<T: std::fmt::Debug> = impl Send; // not a defining use, because it doesn't define *all* possible generics fn underconstrained2<U, V>(_: U, _: V) -> Underconstrained2<V> { diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr index 1c1193705f9..d77d978aa44 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr @@ -1,17 +1,5 @@ -error: at least one trait must be specified - --> $DIR/generic_underconstrained2.rs:5:45 - | -LL | type Underconstrained<T: std::fmt::Debug> = impl 'static; - | ^^^^^^^^^^^^ - -error: at least one trait must be specified - --> $DIR/generic_underconstrained2.rs:14:46 - | -LL | type Underconstrained2<T: std::fmt::Debug> = impl 'static; - | ^^^^^^^^^^^^ - error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:9:33 + --> $DIR/generic_underconstrained2.rs:8:33 | LL | fn underconstrained<U>(_: U) -> Underconstrained<U> { | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` @@ -19,7 +7,7 @@ LL | fn underconstrained<U>(_: U) -> Underconstrained<U> { note: required by a bound in `Underconstrained` --> $DIR/generic_underconstrained2.rs:5:26 | -LL | type Underconstrained<T: std::fmt::Debug> = impl 'static; +LL | type Underconstrained<T: std::fmt::Debug> = impl Send; | ^^^^^^^^^^^^^^^ required by this bound in `Underconstrained` help: consider restricting type parameter `U` | @@ -27,21 +15,21 @@ LL | fn underconstrained<U: std::fmt::Debug>(_: U) -> Underconstrained<U> { | +++++++++++++++++ error[E0277]: `V` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:18:43 + --> $DIR/generic_underconstrained2.rs:16:43 | LL | fn underconstrained2<U, V>(_: U, _: V) -> Underconstrained2<V> { | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound in `Underconstrained2` - --> $DIR/generic_underconstrained2.rs:14:27 + --> $DIR/generic_underconstrained2.rs:13:27 | -LL | type Underconstrained2<T: std::fmt::Debug> = impl 'static; +LL | type Underconstrained2<T: std::fmt::Debug> = impl Send; | ^^^^^^^^^^^^^^^ required by this bound in `Underconstrained2` help: consider restricting type parameter `V` | LL | fn underconstrained2<U, V: std::fmt::Debug>(_: U, _: V) -> Underconstrained2<V> { | +++++++++++++++++ -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/incomplete-inference.rs b/src/test/ui/type-alias-impl-trait/incomplete-inference.rs new file mode 100644 index 00000000000..955d1288a45 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/incomplete-inference.rs @@ -0,0 +1,15 @@ +#![feature(type_alias_impl_trait)] + +type Foo = impl Sized; + +fn bar() -> Foo { + None + //~^ ERROR: type annotations needed [E0282] +} + +fn baz() -> Foo { + //~^ ERROR: concrete type differs from previous defining opaque type use + Some(()) +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/incomplete-inference.stderr b/src/test/ui/type-alias-impl-trait/incomplete-inference.stderr new file mode 100644 index 00000000000..53cdf9e5b38 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/incomplete-inference.stderr @@ -0,0 +1,21 @@ +error[E0282]: type annotations needed + --> $DIR/incomplete-inference.rs:6:5 + | +LL | None + | ^^^^ cannot infer type for type parameter `T` declared on the enum `Option` + +error: concrete type differs from previous defining opaque type use + --> $DIR/incomplete-inference.rs:10:1 + | +LL | fn baz() -> Foo { + | ^^^^^^^^^^^^^^^ expected `[type error]`, got `Option<()>` + | +note: previous use here + --> $DIR/incomplete-inference.rs:5:1 + | +LL | fn bar() -> Foo { + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-dyn.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-dyn.rs new file mode 100644 index 00000000000..f6a83029670 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-dyn.rs @@ -0,0 +1,12 @@ +// check-pass + +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +type Foo = Box<dyn Iterator<Item = impl Send>>; + +fn make_foo() -> Foo { + Box::new(vec![1, 2, 3].into_iter()) +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-impl-trait.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-impl-trait.rs new file mode 100644 index 00000000000..fddecfcacf6 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-assoc-impl-trait.rs @@ -0,0 +1,19 @@ +// check-pass + +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +type Foo = impl Iterator<Item = impl Send>; + +fn make_foo() -> Foo { + vec![1, 2].into_iter() +} + +type Bar = impl Send; +type Baz = impl Iterator<Item = Bar>; + +fn make_baz() -> Baz { + vec!["1", "2"].into_iter() +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.rs new file mode 100644 index 00000000000..299bdf562dc --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.rs @@ -0,0 +1,13 @@ +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +// FIXME: this is ruled out for now but should work + +type Foo = fn() -> impl Send; +//~^ ERROR: `impl Trait` not allowed outside of function and method return types + +fn make_foo() -> Foo { + || 15 +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.stderr new file mode 100644 index 00000000000..1c5d57d4af7 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fn-type.stderr @@ -0,0 +1,9 @@ +error[E0562]: `impl Trait` not allowed outside of function and method return types + --> $DIR/type-alias-impl-trait-fn-type.rs:6:20 + | +LL | type Foo = fn() -> impl Send; + | ^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0562`. diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-struct.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-struct.rs new file mode 100644 index 00000000000..1a4064055db --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-struct.rs @@ -0,0 +1,12 @@ +// check-pass + +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +type Foo = Vec<impl Send>; + +fn make_foo() -> Foo { + vec![true, false] +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs index 86c9d482143..1f2d0e47ea3 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs @@ -7,20 +7,19 @@ pub trait MyTrait {} impl MyTrait for bool {} +type Foo = impl MyTrait; + struct Blah { my_foo: Foo, - my_u8: u8 + my_u8: u8, } impl Blah { fn new() -> Blah { - Blah { - my_foo: make_foo(), - my_u8: 12 - } + Blah { my_foo: make_foo(), my_u8: 12 } } - fn into_inner(self) -> (Foo, u8) { - (self.my_foo, self.my_u8) + fn into_inner(self) -> (Foo, u8, Foo) { + (self.my_foo, self.my_u8, make_foo()) } } @@ -28,6 +27,4 @@ fn make_foo() -> Foo { true } -type Foo = impl MyTrait; - fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.rs index 80192d19af9..d2c8c1f63df 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.rs @@ -11,7 +11,6 @@ fn main() { assert_eq!(bar2().to_string(), "bar2"); let mut x = bar1(); x = bar2(); - assert_eq!(boo::boo().to_string(), "boo"); assert_eq!(my_iter(42u8).collect::<Vec<u8>>(), vec![42u8]); } @@ -33,15 +32,6 @@ fn bar2() -> Bar { "bar2" } -// definition in submodule -type Boo = impl std::fmt::Display; - -mod boo { - pub fn boo() -> super::Boo { - "boo" - } -} - type MyIter<T> = impl Iterator<Item = T>; fn my_iter<T>(t: T) -> MyIter<T> { diff --git a/src/test/ui/type-alias-impl-trait/unused_generic_param.rs b/src/test/ui/type-alias-impl-trait/unused_generic_param.rs index 04a5c58cd36..ad5e4918cca 100644 --- a/src/test/ui/type-alias-impl-trait/unused_generic_param.rs +++ b/src/test/ui/type-alias-impl-trait/unused_generic_param.rs @@ -1,16 +1,17 @@ +// check-pass + #![feature(type_alias_impl_trait)] +#![allow(dead_code)] fn main() {} -type PartiallyDefined<T> = impl 'static; -//~^ ERROR: at least one trait must be specified +type PartiallyDefined<T> = impl Sized; fn partially_defined<T: std::fmt::Debug>(_: T) -> PartiallyDefined<T> { 4u32 } -type PartiallyDefined2<T> = impl 'static; -//~^ ERROR: at least one trait must be specified +type PartiallyDefined2<T> = impl Sized; fn partially_defined2<T: std::fmt::Debug>(_: T) -> PartiallyDefined2<T> { 4u32 diff --git a/src/test/ui/type-alias-impl-trait/unused_generic_param.stderr b/src/test/ui/type-alias-impl-trait/unused_generic_param.stderr deleted file mode 100644 index 4e11854b071..00000000000 --- a/src/test/ui/type-alias-impl-trait/unused_generic_param.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: at least one trait must be specified - --> $DIR/unused_generic_param.rs:5:28 - | -LL | type PartiallyDefined<T> = impl 'static; - | ^^^^^^^^^^^^ - -error: at least one trait must be specified - --> $DIR/unused_generic_param.rs:12:29 - | -LL | type PartiallyDefined2<T> = impl 'static; - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/typeck/issue-87935-unsized-box-expr.rs b/src/test/ui/typeck/issue-87935-unsized-box-expr.rs new file mode 100644 index 00000000000..cd2a82074ed --- /dev/null +++ b/src/test/ui/typeck/issue-87935-unsized-box-expr.rs @@ -0,0 +1,10 @@ +#![feature(box_syntax)] +// Box expression needs to be movable, and hence has to be of a Sized type. +fn main() { + let _x: Box<[u32]> = box { loop {} }; + //~^ ERROR: the size for values of type `[u32]` cannot be known at compilation time + + // Check that a deduced size does not cause issues. + let _y: Box<[u32]> = box []; + let _z: Box<[u32; 0]> = box { loop {} }; +} diff --git a/src/test/ui/typeck/issue-87935-unsized-box-expr.stderr b/src/test/ui/typeck/issue-87935-unsized-box-expr.stderr new file mode 100644 index 00000000000..9ff822352a1 --- /dev/null +++ b/src/test/ui/typeck/issue-87935-unsized-box-expr.stderr @@ -0,0 +1,12 @@ +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/issue-87935-unsized-box-expr.rs:4:30 + | +LL | let _x: Box<[u32]> = box { loop {} }; + | ^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: the type of a box expression must have a statically known size + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unsafe/inline_asm.mir.stderr b/src/test/ui/unsafe/inline_asm.mir.stderr index 7dc62a1ead1..5d9828b5594 100644 --- a/src/test/ui/unsafe/inline_asm.mir.stderr +++ b/src/test/ui/unsafe/inline_asm.mir.stderr @@ -1,5 +1,5 @@ error[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/inline_asm.rs:9:5 + --> $DIR/inline_asm.rs:10:5 | LL | asm!("nop"); | ^^^^^^^^^^^^ use of inline assembly @@ -7,7 +7,7 @@ LL | asm!("nop"); = note: inline assembly is entirely unchecked and can cause undefined behavior error[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/inline_asm.rs:10:5 + --> $DIR/inline_asm.rs:11:5 | LL | llvm_asm!("nop"); | ^^^^^^^^^^^^^^^^^ use of inline assembly diff --git a/src/test/ui/unsafe/inline_asm.rs b/src/test/ui/unsafe/inline_asm.rs index 995292a9903..8e1325bc0a8 100644 --- a/src/test/ui/unsafe/inline_asm.rs +++ b/src/test/ui/unsafe/inline_asm.rs @@ -1,5 +1,6 @@ // revisions: mir thir // [thir]compile-flags: -Z thir-unsafeck +// needs-asm-support #![feature(llvm_asm)] #![feature(asm)] diff --git a/src/test/ui/unsafe/inline_asm.thir.stderr b/src/test/ui/unsafe/inline_asm.thir.stderr index 7dc62a1ead1..5d9828b5594 100644 --- a/src/test/ui/unsafe/inline_asm.thir.stderr +++ b/src/test/ui/unsafe/inline_asm.thir.stderr @@ -1,5 +1,5 @@ error[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/inline_asm.rs:9:5 + --> $DIR/inline_asm.rs:10:5 | LL | asm!("nop"); | ^^^^^^^^^^^^ use of inline assembly @@ -7,7 +7,7 @@ LL | asm!("nop"); = note: inline assembly is entirely unchecked and can cause undefined behavior error[E0133]: use of inline assembly is unsafe and requires unsafe function or block - --> $DIR/inline_asm.rs:10:5 + --> $DIR/inline_asm.rs:11:5 | LL | llvm_asm!("nop"); | ^^^^^^^^^^^^^^^^^ use of inline assembly diff --git a/src/tools/cherry-pick.sh b/src/tools/cherry-pick.sh new file mode 100755 index 00000000000..90539a96389 --- /dev/null +++ b/src/tools/cherry-pick.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n\t' + +print_error() { + echo "Error: \`$1\` is not a valid commit. To debug, run:" + echo + echo " git rev-parse --verify $1" + echo +} + +full_sha() { + git rev-parse \ + --verify \ + --quiet \ + "$1^{object}" || print_error $1 +} + +commit_message_with_backport_note() { + message=$(git log --format=%B -n 1 $1) + echo $message | awk "NR==1{print; print \"\n(backport-of: $1)\"} NR!=1" +} + +cherry_pick_commit() { + sha=$(full_sha $1) + git cherry-pick $sha > /dev/null + git commit \ + --amend \ + --file <(commit_message_with_backport_note $sha) +} + +for arg ; do + cherry_pick_commit $arg +done diff --git a/src/tools/clippy/clippy_lints/src/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/collapsible_match.rs index 6b63c2cf157..a42eee53459 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_match.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{higher, is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq}; +use clippy_utils::{higher, is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, MatchSource, Pat, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -49,104 +49,87 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if let Some(higher::IfLet { - let_pat, - if_then, - if_else, - .. - }) = higher::IfLet::hir(expr) - { - check_arm(cx, if_then, None, let_pat, if_else); - - check_if_let(cx, if_then, let_pat); - } - - if let ExprKind::Match(_expr, arms, _source) = expr.kind { - if let Some(wild_arm) = arms.iter().rfind(|arm| is_wild_like(cx, &arm.pat.kind, &arm.guard)) { - for arm in arms { - check_arm(cx, arm.body, arm.guard.as_ref(), arm.pat, Some(wild_arm.body)); + match IfLetOrMatch::parse(cx, expr) { + Some(IfLetOrMatch::Match(_, arms, _)) => { + if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { + for arm in arms { + check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body)); + } } } - - if let Some(first_arm) = arms.get(0) { - check_if_let(cx, &first_arm.body, &first_arm.pat); + Some(IfLetOrMatch::IfLet(_, pat, body, els)) => { + check_arm(cx, false, pat, body, None, els); } + None => {} } } } fn check_arm<'tcx>( cx: &LateContext<'tcx>, - outer_block: &'tcx Expr<'tcx>, - outer_guard: Option<&Guard<'tcx>>, + outer_is_match: bool, outer_pat: &'tcx Pat<'tcx>, - wild_outer_block: Option<&'tcx Expr<'tcx>>, + outer_then_body: &'tcx Expr<'tcx>, + outer_guard: Option<&'tcx Guard<'tcx>>, + outer_else_body: Option<&'tcx Expr<'tcx>> ) { - let expr = strip_singleton_blocks(outer_block); + let inner_expr = strip_singleton_blocks(outer_then_body); if_chain! { - if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind; - // the outer arm pattern and the inner match - if expr_in.span.ctxt() == outer_pat.span.ctxt(); - // there must be no more than two arms in the inner match for this lint - if arms_inner.len() == 2; - // no if guards on the inner match - if arms_inner.iter().all(|arm| arm.guard.is_none()); + if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr); + if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { + IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)), + IfLetOrMatch::Match(scrutinee, arms, ..) => if_chain! { + // if there are more than two arms, collapsing would be non-trivial + if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()); + // one of the arms must be "wild-like" + if let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)); + then { + let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); + Some((scrutinee, then.pat, Some(els.body))) + } else { + None + } + }, + }; + if outer_pat.span.ctxt() == inner_scrutinee.span.ctxt(); // match expression must be a local binding // match <local> { .. } - if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in)); - // one of the branches must be "wild-like" - if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| is_wild_like(cx, &arm_inner.pat.kind, &arm_inner.guard)); - let (wild_inner_arm, non_wild_inner_arm) = - (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); - if !pat_contains_or(non_wild_inner_arm.pat); + if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)); + if !pat_contains_or(inner_then_pat); // the binding must come from the pattern of the containing match arm // ..<local>.. => match <local> { .. } if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); - // the "wild-like" branches must be equal - if wild_outer_block.map(|el| SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, el)).unwrap_or(true); + // the "else" branches must be equal + if match (outer_else_body, inner_else_body) { + (None, None) => true, + (None, Some(e)) | (Some(e), None) => is_unit_expr(e), + (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), + }; // the binding must not be used in the if guard let mut used_visitor = LocalUsedVisitor::new(cx, binding_id); - if match outer_guard { - None => true, - Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr), + if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !used_visitor.check_expr(e)); + // ...or anywhere in the inner expression + if match inner { + IfLetOrMatch::IfLet(_, _, body, els) => { + !used_visitor.check_expr(body) && els.map_or(true, |e| !used_visitor.check_expr(e)) + }, + IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| used_visitor.check_arm(arm)), }; - // ...or anywhere in the inner match - if !arms_inner.iter().any(|arm| used_visitor.check_arm(arm)); then { - span_lint_and_then( - cx, - COLLAPSIBLE_MATCH, - expr.span, - "unnecessary nested match", - |diag| { - let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]); - help_span.push_span_label(binding_span, "replace this binding".into()); - help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into()); - diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); - }, + let msg = format!( + "this `{}` can be collapsed into the outer `{}`", + if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, + if outer_is_match { "match" } else { "if let" }, ); - } - } -} - -fn check_if_let<'tcx>(cx: &LateContext<'tcx>, outer_expr: &'tcx Expr<'tcx>, outer_pat: &'tcx Pat<'tcx>) { - let block_inner = strip_singleton_blocks(outer_expr); - if_chain! { - if let Some(higher::IfLet { if_then: inner_if_then, let_expr: inner_let_expr, let_pat: inner_let_pat, .. }) = higher::IfLet::hir(block_inner); - if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_let_expr)); - if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); - let mut used_visitor = LocalUsedVisitor::new(cx, binding_id); - if !used_visitor.check_expr(inner_if_then); - then { span_lint_and_then( cx, COLLAPSIBLE_MATCH, - block_inner.span, - "unnecessary nested `if let` or `match`", + inner_expr.span, + &msg, |diag| { - let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_let_pat.span]); + let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding".into()); - help_span.push_span_label(inner_let_pat.span, "with this pattern".into()); + help_span.push_span_label(inner_then_pat.span, "with this pattern".into()); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); @@ -168,14 +151,30 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> expr } -/// A "wild-like" pattern is wild ("_") or `None`. -/// For this lint to apply, both the outer and inner patterns -/// must have "wild-like" branches that can be combined. -fn is_wild_like(cx: &LateContext<'_>, pat_kind: &PatKind<'_>, arm_guard: &Option<Guard<'_>>) -> bool { - if arm_guard.is_some() { +enum IfLetOrMatch<'hir> { + Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource), + /// scrutinee, pattern, then block, else block + IfLet(&'hir Expr<'hir>, &'hir Pat<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>), +} + +impl<'hir> IfLetOrMatch<'hir> { + fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> { + match expr.kind { + ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)), + _ => higher::IfLet::hir(cx, expr).map(|higher::IfLet { let_expr, let_pat, if_then, if_else }| { + Self::IfLet(let_expr, let_pat, if_then, if_else) + }) + } + } +} + +/// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed" +/// into a single wild arm without any significant loss in semantics or readability. +fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + if arm.guard.is_some() { return false; } - match pat_kind { + match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), _ => false, diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index e2d3905eacb..7dad1c31150 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { if_then, if_else: Some(if_else), .. - }) = higher::IfLet::hir(expr) + }) = higher::IfLet::hir(cx, expr) { op_visit.visit_expr(let_expr); if op_visit.mutex_lock_called { diff --git a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs index cd813c639db..fb5637fcec1 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs @@ -45,7 +45,7 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables - if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr); + if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr); if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = let_pat.kind; //get operation if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs index 9f2bc3c7eba..5852674da57 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(inner_expr) = inner_expr; - if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(inner_expr); + if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(cx, inner_expr); // Ensure match_expr in `if let` statement is the same as the pat from the for-loop if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; if path_to_local_id(let_expr, pat_hir_id); diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index 6be410ca8e3..d6d3315e0a8 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &' let_expr, if_else: Some(if_else), .. - }) = higher::IfLet::hir(inner) + }) = higher::IfLet::hir(cx, inner) { if is_simple_break_expr(if_else) { could_be_while_let(cx, expr, let_pat, let_expr); diff --git a/src/tools/clippy/clippy_lints/src/manual_map.rs b/src/tools/clippy/clippy_lints/src/manual_map.rs index 53d97f77543..161d8841490 100644 --- a/src/tools/clippy/clippy_lints/src/manual_map.rs +++ b/src/tools/clippy/clippy_lints/src/manual_map.rs @@ -49,7 +49,7 @@ impl LateLintPass<'_> for ManualMap { let_expr, if_then, if_else: Some(if_else), - }) = higher::IfLet::hir(expr) + }) = higher::IfLet::hir(cx, expr) { manage_lint(cx, expr, (&let_pat.kind, if_then), (&PatKind::Wild, if_else), let_expr); } diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index a183d0c66e8..3d0da472ddc 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -8,9 +8,9 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::LocalUsedVisitor; use clippy_utils::{ - get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_wild, meets_msrv, msrvs, - path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, - strip_pat_refs, + get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, + meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, + remove_blocks, strip_pat_refs, }; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use core::array; @@ -634,7 +634,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if let ExprKind::Match(ref ex, ref arms, _) = expr.kind { check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr); } - if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr) { + if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) { check_match_ref_pats(cx, let_expr, once(let_pat), expr); } } @@ -1298,7 +1298,7 @@ fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) let_expr, if_then, if_else: Some(if_else), - }) = higher::IfLet::hir(expr) + }) = higher::IfLet::hir(cx, expr) { return find_matches_sugg( cx, @@ -1672,14 +1672,6 @@ fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges { .collect() } -fn is_unit_expr(expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Tup(v) if v.is_empty() => true, - ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none() => true, - _ => false, - } -} - // Checks if arm has the form `None => None` fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) @@ -1835,7 +1827,7 @@ mod redundant_pattern_match { let_pat, let_expr, .. - }) = higher::IfLet::ast(cx, expr) + }) = higher::IfLet::hir(cx, expr) { find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()) } diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index d0b0bad5eb1..eff3d3abff8 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -125,7 +125,7 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> { if_chain! { if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); if !is_else_clause(cx.tcx, expr); if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind; diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 91085c13ac4..7b6a0894e6d 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -97,7 +97,7 @@ impl QuestionMark { fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); if Self::is_option(cx, let_expr); if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index ee675838c4c..db0f412f2a1 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -127,7 +127,7 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa then { let data = stmt.span.data(); // Make a span out of the semicolon for the help message - Some((span, Some(Span::new(data.hi-BytePos(1), data.hi, data.ctxt)))) + Some((span, Some(data.with_lo(data.hi-BytePos(1))))) } else { Some((span, None)) } diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 4553ac704a2..85d1f65c51f 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -299,7 +299,7 @@ impl EarlyLintPass for Write { let nl_span = match (dest, only_nl) { // Special case of `write!(buf, "\n")`: Mark everything from the end of // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains. - (Some(dest_expr), true) => Span::new(dest_expr.span.hi(), nl_span.hi(), nl_span.ctxt()), + (Some(dest_expr), true) => nl_span.with_lo(dest_expr.span.hi()), _ => nl_span, }; span_lint_and_then( diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 29b698e56e3..957ec35be6f 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -79,15 +79,7 @@ pub struct IfLet<'hir> { } impl<'hir> IfLet<'hir> { - #[inline] - pub fn ast(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option<Self> { - let rslt = Self::hir(expr)?; - Self::is_not_within_while_context(cx, expr)?; - Some(rslt) - } - - #[inline] - pub const fn hir(expr: &Expr<'hir>) -> Option<Self> { + pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> { if let ExprKind::If( Expr { kind: ExprKind::Let(let_pat, let_expr, _), @@ -97,6 +89,14 @@ impl<'hir> IfLet<'hir> { if_else, ) = expr.kind { + let hir = cx.tcx.hir(); + let mut iter = hir.parent_iter(expr.hir_id); + if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() { + if let Some((_, Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::While, _), .. }))) = iter.next() { + // while loop desugar + return None; + } + } return Some(Self { let_pat, let_expr, @@ -106,22 +106,6 @@ impl<'hir> IfLet<'hir> { } None } - - #[inline] - fn is_not_within_while_context(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option<()> { - let hir = cx.tcx.hir(); - let parent = hir.get_parent_node(expr.hir_id); - let parent_parent = hir.get_parent_node(parent); - let parent_parent_node = hir.get(parent_parent); - if let Node::Expr(Expr { - kind: ExprKind::Loop(_, _, LoopSource::While, _), - .. - }) = parent_parent_node - { - return None; - } - Some(()) - } } pub struct IfOrIfLet<'hir> { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 82bfce8fe78..2777e5bd0c4 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -254,6 +254,10 @@ pub fn in_macro(span: Span) -> bool { } } +pub fn is_unit_expr(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Block(Block { stmts: [], expr: None, .. }, _) | ExprKind::Tup([])) +} + /// Checks if given pattern is a wildcard (`_`) pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) @@ -877,7 +881,7 @@ fn line_span<T: LintContext>(cx: &T, span: Span) -> Span { let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); let line_no = source_map_and_line.line; let line_start = source_map_and_line.sf.lines[line_no]; - Span::new(line_start, span.hi(), span.ctxt()) + span.with_lo(line_start) } /// Gets the parent node, if any. diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index dee9d487c78..8e544f58066 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -36,6 +36,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), + ty::PredicateKind::Coerce(_) => { + panic!("coerce predicate on function: {:#?}", predicate) + }, ty::PredicateKind::Trait(pred) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; diff --git a/src/tools/clippy/tests/ui/collapsible_match.rs b/src/tools/clippy/tests/ui/collapsible_match.rs index 55467cf4229..4ce365cc764 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.rs +++ b/src/tools/clippy/tests/ui/collapsible_match.rs @@ -98,6 +98,11 @@ fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String> } fn negative_cases(res_opt: Result<Option<u32>, String>, res_res: Result<Result<u32, String>, String>) { + while let Some(x) = make() { + if let Some(1) = x { + todo!(); + } + } // no wild pattern in outer match match res_opt { Ok(val) => match val { diff --git a/src/tools/clippy/tests/ui/collapsible_match.stderr b/src/tools/clippy/tests/ui/collapsible_match.stderr index f96917f5833..c119570e8ab 100644 --- a/src/tools/clippy/tests/ui/collapsible_match.stderr +++ b/src/tools/clippy/tests/ui/collapsible_match.stderr @@ -1,4 +1,4 @@ -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:7:20 | LL | Ok(val) => match val { @@ -17,7 +17,7 @@ LL | Ok(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:16:20 | LL | Ok(val) => match val { @@ -35,7 +35,7 @@ LL | Ok(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested `if let` or `match` +error: this `if let` can be collapsed into the outer `if let` --> $DIR/collapsible_match.rs:25:9 | LL | / if let Some(n) = val { @@ -51,7 +51,7 @@ LL | if let Ok(val) = res_opt { LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: unnecessary nested `if let` or `match` +error: this `if let` can be collapsed into the outer `if let` --> $DIR/collapsible_match.rs:32:9 | LL | / if let Some(n) = val { @@ -69,7 +69,7 @@ LL | if let Ok(val) = res_opt { LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `if let` --> $DIR/collapsible_match.rs:43:9 | LL | / match val { @@ -87,7 +87,7 @@ LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested `if let` or `match` +error: this `if let` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:52:13 | LL | / if let Some(n) = val { @@ -103,7 +103,7 @@ LL | Ok(val) => { LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `if let` --> $DIR/collapsible_match.rs:61:9 | LL | / match val { @@ -121,7 +121,7 @@ LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested `if let` or `match` +error: this `if let` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:72:13 | LL | / if let Some(n) = val { @@ -139,7 +139,7 @@ LL | Ok(val) => { LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:83:20 | LL | Ok(val) => match val { @@ -157,7 +157,7 @@ LL | Ok(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match.rs:92:22 | LL | Some(val) => match val { diff --git a/src/tools/clippy/tests/ui/collapsible_match2.stderr b/src/tools/clippy/tests/ui/collapsible_match2.stderr index 8975b2efbae..55e70dce208 100644 --- a/src/tools/clippy/tests/ui/collapsible_match2.stderr +++ b/src/tools/clippy/tests/ui/collapsible_match2.stderr @@ -1,4 +1,4 @@ -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:13:34 | LL | Ok(val) if make() => match val { @@ -17,7 +17,7 @@ LL | Ok(val) if make() => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:20:24 | LL | Ok(val) => match val { @@ -35,7 +35,7 @@ LL | Ok(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:34:29 | LL | $pat => match $e { @@ -57,7 +57,7 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | replace this binding = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:51:20 | LL | Some(s) => match *s { @@ -75,7 +75,7 @@ LL | Some(s) => match *s { LL | [n] => foo(n), | ^^^ with this pattern -error: unnecessary nested match +error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:60:24 | LL | Some(ref s) => match &*s { diff --git a/src/tools/clippy/tests/ui/if_let_some_result.fixed b/src/tools/clippy/tests/ui/if_let_some_result.fixed index 62a25ce2d12..1bddc47721e 100644 --- a/src/tools/clippy/tests/ui/if_let_some_result.fixed +++ b/src/tools/clippy/tests/ui/if_let_some_result.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::if_let_some_result)] +#![allow(dead_code)] fn str_to_int(x: &str) -> i32 { if let Ok(y) = x.parse() { y } else { 0 } @@ -20,8 +21,8 @@ fn strange_some_no_else(x: &str) -> i32 { } } -fn main() { - let _ = str_to_int("1"); - let _ = str_to_int_ok("2"); - let _ = strange_some_no_else("3"); +fn negative() { + while let Some(1) = "".parse().ok() {} } + +fn main() {} diff --git a/src/tools/clippy/tests/ui/if_let_some_result.rs b/src/tools/clippy/tests/ui/if_let_some_result.rs index 234ff5e9e80..d4a52ec9881 100644 --- a/src/tools/clippy/tests/ui/if_let_some_result.rs +++ b/src/tools/clippy/tests/ui/if_let_some_result.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::if_let_some_result)] +#![allow(dead_code)] fn str_to_int(x: &str) -> i32 { if let Some(y) = x.parse().ok() { y } else { 0 } @@ -20,8 +21,8 @@ fn strange_some_no_else(x: &str) -> i32 { } } -fn main() { - let _ = str_to_int("1"); - let _ = str_to_int_ok("2"); - let _ = strange_some_no_else("3"); +fn negative() { + while let Some(1) = "".parse().ok() {} } + +fn main() {} diff --git a/src/tools/clippy/tests/ui/if_let_some_result.stderr b/src/tools/clippy/tests/ui/if_let_some_result.stderr index 134ce9d2411..bc3a5e7698d 100644 --- a/src/tools/clippy/tests/ui/if_let_some_result.stderr +++ b/src/tools/clippy/tests/ui/if_let_some_result.stderr @@ -1,5 +1,5 @@ error: matching on `Some` with `ok()` is redundant - --> $DIR/if_let_some_result.rs:6:5 + --> $DIR/if_let_some_result.rs:7:5 | LL | if let Some(y) = x.parse().ok() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let Ok(y) = x.parse() { y } else { 0 } | ~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant - --> $DIR/if_let_some_result.rs:16:9 + --> $DIR/if_let_some_result.rs:17:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer -Subproject 5664a2b0b31403024ce5ab927760d630d5ddc9a +Subproject b641a66078ce2f2363e9a3b050ba448b93fb7cb |
