From 4c4e43035f8b8bcc171e4a359bb5b42108eadf41 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 4 Feb 2022 13:33:06 -0800 Subject: [PATCH 01/16] Rename `BorrowedFd::borrow_raw_fd` to `BorrowedFd::borrow_raw`. Also, rename `BorrowedHandle::borrow_raw_handle` and `BorrowedSocket::borrow_raw_socket` to `BorrowedHandle::borrow_raw` and `BorrowedSocket::borrow_raw`. This is just a minor rename to reduce redundancy in the user code calling these functions, and to eliminate an inessential difference between `BorrowedFd` code and `BorrowedHandle`/`BorrowedSocket` code. While here, add a simple test exercising `BorrowedFd::borrow_raw_fd`. --- library/std/src/os/fd/mod.rs | 3 +++ library/std/src/os/fd/owned.rs | 4 +-- library/std/src/os/fd/tests.rs | 34 +++++++++++++++++++++++++ library/std/src/os/windows/io/handle.rs | 24 ++++++++--------- library/std/src/os/windows/io/socket.rs | 10 ++++---- library/std/src/sys/unix/stdio.rs | 12 ++++----- 6 files changed, 62 insertions(+), 25 deletions(-) create mode 100644 library/std/src/os/fd/tests.rs diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index df11dc21aa7a6..13bb079194fbe 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -11,3 +11,6 @@ pub mod owned; // Implementations for `AsRawFd` etc. for network types. mod net; + +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 0b6588db92c83..1b3739d33f395 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -62,7 +62,7 @@ impl BorrowedFd<'_> { /// 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 { + pub unsafe fn borrow_raw(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 } } @@ -215,7 +215,7 @@ impl AsFd for OwnedFd { // 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()) } + unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } } } diff --git a/library/std/src/os/fd/tests.rs b/library/std/src/os/fd/tests.rs new file mode 100644 index 0000000000000..be920a2b38c65 --- /dev/null +++ b/library/std/src/os/fd/tests.rs @@ -0,0 +1,34 @@ +#[cfg(any(unix, target_os = "wasi"))] +#[test] +fn test_raw_fd() { + #[cfg(unix)] + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd, BorrowedFd}; + #[cfg(target_os = "wasi")] + use crate::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd, BorrowedFd}; + + let raw_fd: RawFd = crate::io::stdin().as_raw_fd(); + + let stdin_as_file = unsafe { crate::fs::File::from_raw_fd(raw_fd) }; + assert_eq!(stdin_as_file.as_raw_fd(), raw_fd); + assert_eq!(unsafe { BorrowedFd::borrow_raw(raw_fd).as_raw_fd() }, raw_fd); + assert_eq!(stdin_as_file.into_raw_fd(), 0); +} + +#[cfg(any(unix, target_os = "wasi"))] +#[test] +fn test_fd() { + #[cfg(unix)] + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd, FromRawFd, IntoRawFd, RawFd, AsRawFd}; + #[cfg(target_os = "wasi")] + use crate::os::wasi::io::{AsFd, BorrowedFd, OwnedFd, FromRawFd, IntoRawFd, RawFd, AsRawFd}; + + let stdin = crate::io::stdin(); + let fd: BorrowedFd<'_> = stdin.as_fd(); + let raw_fd: RawFd = fd.as_raw_fd(); + let owned_fd: OwnedFd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; + + let stdin_as_file = crate::fs::File::from(owned_fd); + + assert_eq!(stdin_as_file.as_fd().as_raw_fd(), raw_fd); + assert_eq!(Into::::into(stdin_as_file).into_raw_fd(), raw_fd); +} diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index e37ce633a129a..5c224f118b6ea 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -131,7 +131,7 @@ impl BorrowedHandle<'_> { /// [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 { + pub unsafe fn borrow_raw(handle: RawHandle) -> Self { Self { handle, _phantom: PhantomData } } } @@ -329,7 +329,7 @@ impl AsHandle for OwnedHandle { // 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()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } @@ -357,49 +357,49 @@ impl From for fs::File { impl AsHandle for crate::io::Stdin { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(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()) } + unsafe { BorrowedHandle::borrow_raw(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()) } + unsafe { BorrowedHandle::borrow_raw(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()) } + unsafe { BorrowedHandle::borrow_raw(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()) } + unsafe { BorrowedHandle::borrow_raw(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()) } + unsafe { BorrowedHandle::borrow_raw(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()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } @@ -413,7 +413,7 @@ impl From for OwnedHandle { impl AsHandle for crate::process::ChildStdout { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } @@ -427,7 +427,7 @@ impl From for OwnedHandle { impl AsHandle for crate::process::ChildStderr { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } @@ -441,7 +441,7 @@ impl From for OwnedHandle { impl AsHandle for crate::thread::JoinHandle { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 26b569bcdd362..6ed44b4b11e61 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -67,7 +67,7 @@ impl BorrowedSocket<'_> { /// `INVALID_SOCKET`. #[inline] #[unstable(feature = "io_safety", issue = "87074")] - pub unsafe fn borrow_raw_socket(socket: RawSocket) -> Self { + pub unsafe fn borrow_raw(socket: RawSocket) -> Self { debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket); Self { socket, _phantom: PhantomData } } @@ -223,14 +223,14 @@ impl AsSocket for OwnedSocket { // 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()) } + unsafe { BorrowedSocket::borrow_raw(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()) } + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } @@ -251,7 +251,7 @@ impl From for crate::net::TcpStream { impl AsSocket for crate::net::TcpListener { #[inline] fn as_socket(&self) -> BorrowedSocket<'_> { - unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } @@ -272,7 +272,7 @@ impl From for crate::net::TcpListener { impl AsSocket for crate::net::UdpSocket { #[inline] fn as_socket(&self) -> BorrowedSocket<'_> { - unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index b359987595d30..e4d83ba0ffd13 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -96,7 +96,7 @@ pub fn panic_output() -> Option { impl AsFd for io::Stdin { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) } } } @@ -104,7 +104,7 @@ impl AsFd for io::Stdin { impl<'a> AsFd for io::StdinLock<'a> { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) } } } @@ -112,7 +112,7 @@ impl<'a> AsFd for io::StdinLock<'a> { impl AsFd for io::Stdout { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) } } } @@ -120,7 +120,7 @@ impl AsFd for io::Stdout { impl<'a> AsFd for io::StdoutLock<'a> { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) } } } @@ -128,7 +128,7 @@ impl<'a> AsFd for io::StdoutLock<'a> { impl AsFd for io::Stderr { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) } } } @@ -136,6 +136,6 @@ impl AsFd for io::Stderr { impl<'a> AsFd for io::StderrLock<'a> { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) } } } From 7d603dc1b2330713a8739b933ec4fc564ee1127c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 4 Feb 2022 13:53:58 -0800 Subject: [PATCH 02/16] x.py fmt --- library/std/src/os/fd/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/std/src/os/fd/tests.rs b/library/std/src/os/fd/tests.rs index be920a2b38c65..26ef93e3d7110 100644 --- a/library/std/src/os/fd/tests.rs +++ b/library/std/src/os/fd/tests.rs @@ -2,9 +2,9 @@ #[test] fn test_raw_fd() { #[cfg(unix)] - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd, BorrowedFd}; + use crate::os::unix::io::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; #[cfg(target_os = "wasi")] - use crate::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd, BorrowedFd}; + use crate::os::wasi::io::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; let raw_fd: RawFd = crate::io::stdin().as_raw_fd(); @@ -18,9 +18,9 @@ fn test_raw_fd() { #[test] fn test_fd() { #[cfg(unix)] - use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd, FromRawFd, IntoRawFd, RawFd, AsRawFd}; + use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(target_os = "wasi")] - use crate::os::wasi::io::{AsFd, BorrowedFd, OwnedFd, FromRawFd, IntoRawFd, RawFd, AsRawFd}; + use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; let stdin = crate::io::stdin(); let fd: BorrowedFd<'_> = stdin.as_fd(); From 4c7fb9efb7804e489bcf4fe1d3cdf0f7df3b0eff Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 19:01:44 +0300 Subject: [PATCH 03/16] Add helper function to suggest multiple constraints Add `rustc_middle::ty::suggest_constraining_type_params` that suggests adding multiple constraints. `suggest_constraining_type_param` now just forwards params to this new function. --- compiler/rustc_middle/src/lib.rs | 1 + compiler/rustc_middle/src/ty/diagnostics.rs | 398 ++++++++++++-------- 2 files changed, 236 insertions(+), 163 deletions(-) diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 7ca564f29e659..f977b0fffebb6 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -56,6 +56,7 @@ #![feature(nonzero_ops)] #![feature(unwrap_infallible)] #![feature(decl_macro)] +#![feature(drain_filter)] #![recursion_limit = "512"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 58cf9fa7a8943..99a3d4c7fe4f7 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -7,6 +7,7 @@ use crate::ty::{ ProjectionTy, Term, Ty, TyCtxt, TypeAndMut, }; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -157,9 +158,17 @@ pub fn suggest_arbitrary_trait_bound( true } +#[derive(Debug)] +enum SuggestChangingConstraintsMessage<'a> { + RestrictBoundFurther, + RestrictType { ty: &'a str }, + RestrictTypeFurther { ty: &'a str }, + RemovingQSized, +} + fn suggest_removing_unsized_bound( generics: &hir::Generics<'_>, - err: &mut Diagnostic, + suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>, param_name: &str, param: &hir::GenericParam<'_>, def_id: Option, @@ -221,13 +230,12 @@ fn suggest_removing_unsized_bound( // ^^^^^^^^^ (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()), }; - err.span_suggestion_verbose( + + suggestions.push(( sp, - "consider removing the `?Sized` bound to make the \ - type parameter `Sized`", String::new(), - Applicability::MaybeIncorrect, - ); + SuggestChangingConstraintsMessage::RemovingQSized, + )); } } _ => {} @@ -249,13 +257,12 @@ fn suggest_removing_unsized_bound( // ^^^^^^^^^ (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()), }; - err.span_suggestion_verbose( + + suggestions.push(( sp, - "consider removing the `?Sized` bound to make the type parameter \ - `Sized`", String::new(), - Applicability::MaybeIncorrect, - ); + SuggestChangingConstraintsMessage::RemovingQSized, + )); } _ => {} } @@ -271,184 +278,249 @@ pub fn suggest_constraining_type_param( constraint: &str, def_id: Option, ) -> bool { - let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + suggest_constraining_type_params( + tcx, + generics, + err, + [(param_name, constraint, def_id)].into_iter(), + ) +} - let Some(param) = param else { - return false; - }; +/// Suggest restricting a type param with a new bound. +pub fn suggest_constraining_type_params<'a>( + tcx: TyCtxt<'_>, + generics: &hir::Generics<'_>, + err: &mut Diagnostic, + param_names_and_constraints: impl Iterator)>, +) -> bool { + let mut grouped = FxHashMap::default(); + param_names_and_constraints.for_each(|(param_name, constraint, def_id)| { + grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id)) + }); - const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound"; - let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name); - let msg_restrict_type_further = - format!("consider further restricting type parameter `{}`", param_name); + let mut applicability = Applicability::MachineApplicable; + let mut suggestions = Vec::new(); - if def_id == tcx.lang_items().sized_trait() { - // Type parameters are already `Sized` by default. - err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); - suggest_removing_unsized_bound(generics, err, param_name, param, def_id); - return true; - } - let mut suggest_restrict = |span| { - err.span_suggestion_verbose( - span, - MSG_RESTRICT_BOUND_FURTHER, - format!(" + {}", constraint), - Applicability::MachineApplicable, - ); - }; + for (param_name, mut constraints) in grouped { + let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + let Some(param) = param else { return false }; - if param_name.starts_with("impl ") { - // If there's an `impl Trait` used in argument position, suggest - // restricting it: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // replace with: `impl Foo + Bar` - - suggest_restrict(param.span.shrink_to_hi()); - return true; - } + { + let mut sized_constraints = + constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait()); + if let Some((constraint, def_id)) = sized_constraints.next() { + applicability = Applicability::MaybeIncorrect; - if generics.where_clause.predicates.is_empty() - // Given `trait Base: Super` where `T: Copy`, suggest restricting in the - // `where` clause instead of `trait Base: Super`. - && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) - { - if let Some(span) = param.bounds_span_for_suggestions() { - // If user has provided some bounds, suggest restricting them: + err.span_label( + param.span, + &format!("this type parameter needs to be `{}`", constraint), + ); + suggest_removing_unsized_bound( + generics, + &mut suggestions, + param_name, + param, + def_id, + ); + } + } + + if constraints.is_empty() { + continue; + } + + let constraint = constraints.iter().map(|&(c, _)| c).collect::>().join(" + "); + let mut suggest_restrict = |span| { + suggestions.push(( + span, + format!(" + {}", constraint), + SuggestChangingConstraintsMessage::RestrictBoundFurther, + )) + }; + + if param_name.starts_with("impl ") { + // If there's an `impl Trait` used in argument position, suggest + // restricting it: // - // fn foo(t: T) { ... } - // --- + // fn foo(t: impl Foo) { ... } + // -------- // | // help: consider further restricting this bound with `+ Bar` // // Suggestion for tools in this case is: // - // fn foo(t: T) { ... } - // -- - // | - // replace with: `T: Bar +` - suggest_restrict(span); - } else { - // If user hasn't provided any bounds, suggest adding a new one: - // - // fn foo(t: T) { ... } - // - help: consider restricting this type parameter with `T: Foo` - err.span_suggestion_verbose( - param.span.shrink_to_hi(), - &msg_restrict_type, - format!(": {}", constraint), - Applicability::MachineApplicable, - ); + // fn foo(t: impl Foo) { ... } + // -------- + // | + // replace with: `impl Foo + Bar` + + suggest_restrict(param.span.shrink_to_hi()); + continue; } - true - } else { - // This part is a bit tricky, because using the `where` clause user can - // provide zero, one or many bounds for the same type parameter, so we - // have following cases to consider: - // - // 1) When the type parameter has been provided zero bounds - // - // Message: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - help: consider restricting this type parameter with `where X: Bar` - // - // Suggestion: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - insert: `, X: Bar` - // - // - // 2) When the type parameter has been provided one bound - // - // Message: - // fn foo(t: T) where T: Foo { ... } - // ^^^^^^ - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion: - // fn foo(t: T) where T: Foo { ... } - // ^^ - // | - // replace with: `T: Bar +` - // - // - // 3) When the type parameter has been provided many bounds - // - // Message: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - insert: `, T: Zar` - // - // Additionally, there may be no `where` clause whatsoever in the case that this was - // reached because the generic parameter has a default: - // - // Message: - // trait Foo {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // trait Foo where T: Zar {... } - // - insert: `where T: Zar` - - if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) - && generics.where_clause.predicates.len() == 0 + if generics.where_clause.predicates.is_empty() + // Given `trait Base: Super` where `T: Copy`, suggest restricting in the + // `where` clause instead of `trait Base: Super`. + && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) { - // Suggest a bound, but there is no existing `where` clause *and* the type param has a - // default (``), so we suggest adding `where T: Bar`. - err.span_suggestion_verbose( - generics.where_clause.tail_span_for_suggestion(), - &msg_restrict_type_further, - format!(" where {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); + if let Some(span) = param.bounds_span_for_suggestions() { + // If user has provided some bounds, suggest restricting them: + // + // fn foo(t: T) { ... } + // --- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: T) { ... } + // -- + // | + // replace with: `T: Bar +` + suggest_restrict(span); + } else { + // If user hasn't provided any bounds, suggest adding a new one: + // + // fn foo(t: T) { ... } + // - help: consider restricting this type parameter with `T: Foo` + suggestions.push(( + param.span.shrink_to_hi(), + format!(": {}", constraint), + SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, + )); + } } else { - let mut param_spans = Vec::new(); + // This part is a bit tricky, because using the `where` clause user can + // provide zero, one or many bounds for the same type parameter, so we + // have following cases to consider: + // + // 1) When the type parameter has been provided zero bounds + // + // Message: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - help: consider restricting this type parameter with `where X: Bar` + // + // Suggestion: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - insert: `, X: Bar` + // + // + // 2) When the type parameter has been provided one bound + // + // Message: + // fn foo(t: T) where T: Foo { ... } + // ^^^^^^ + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion: + // fn foo(t: T) where T: Foo { ... } + // ^^ + // | + // replace with: `T: Bar +` + // + // + // 3) When the type parameter has been provided many bounds + // + // Message: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - insert: `, T: Zar` + // + // Additionally, there may be no `where` clause whatsoever in the case that this was + // reached because the generic parameter has a default: + // + // Message: + // trait Foo {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // trait Foo where T: Zar {... } + // - insert: `where T: Zar` - for predicate in generics.where_clause.predicates { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - span, - bounded_ty, - .. - }) = predicate - { - if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { - if let Some(segment) = path.segments.first() { - if segment.ident.to_string() == param_name { - param_spans.push(span); + if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) + && generics.where_clause.predicates.len() == 0 + { + // Suggest a bound, but there is no existing `where` clause *and* the type param has a + // default (``), so we suggest adding `where T: Bar`. + suggestions.push(( + generics.where_clause.tail_span_for_suggestion(), + format!(" where {}: {}", param_name, constraint), + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, + )); + } else { + let mut param_spans = Vec::new(); + + for predicate in generics.where_clause.predicates { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { + span, + bounded_ty, + .. + }) = predicate + { + if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { + if let Some(segment) = path.segments.first() { + if segment.ident.to_string() == param_name { + param_spans.push(span); + } } } } } - } - match param_spans[..] { - [¶m_span] => suggest_restrict(param_span.shrink_to_hi()), - _ => { - err.span_suggestion_verbose( - generics.where_clause.tail_span_for_suggestion(), - &msg_restrict_type_further, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); + match param_spans[..] { + [¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + _ => { + suggestions.push(( + generics.where_clause.tail_span_for_suggestion(), + constraints + .iter() + .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint)) + .collect::(), + SuggestChangingConstraintsMessage::RestrictTypeFurther { + ty: param_name, + }, + )); + } } } } + } - true + if suggestions.len() == 1 { + let (span, suggestion, msg) = suggestions.pop().unwrap(); + + let s; + let msg = match msg { + SuggestChangingConstraintsMessage::RestrictBoundFurther => { + "consider further restricting this bound" + } + SuggestChangingConstraintsMessage::RestrictType { ty } => { + s = format!("consider restricting type parameter `{}`", ty); + &s + } + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => { + s = format!("consider further restricting type parameter `{}`", ty); + &s + } + SuggestChangingConstraintsMessage::RemovingQSized => { + "consider removing the `?Sized` bound to make the type parameter `Sized`" + } + }; + + err.span_suggestion_verbose(span, msg, suggestion, applicability); + } else { + err.multipart_suggestion_verbose( + "consider restricting type parameters", + suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(), + applicability, + ); } + + true } /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. From 765205b9b8274f6437d3fee291bc80e7e61a0af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20BRANSTETT?= Date: Sun, 27 Feb 2022 22:26:24 +0100 Subject: [PATCH 04/16] Improve allowness of the unexpected_cfgs lint --- compiler/rustc_attr/src/builtin.rs | 14 +++++++---- compiler/rustc_builtin_macros/src/cfg.rs | 7 +++++- compiler/rustc_builtin_macros/src/cfg_eval.rs | 6 +++-- compiler/rustc_builtin_macros/src/derive.rs | 7 +++++- compiler/rustc_codegen_ssa/src/back/link.rs | 3 ++- compiler/rustc_expand/src/config.rs | 20 ++++++++++++---- compiler/rustc_expand/src/expand.rs | 23 +++++++++++-------- compiler/rustc_interface/src/passes.rs | 3 ++- compiler/rustc_metadata/src/native_libs.rs | 3 ++- src/test/ui/check-cfg/allow-macro-cfg.rs | 14 +++++++++++ src/test/ui/check-cfg/allow-same-level.rs | 11 +++++++++ src/test/ui/check-cfg/allow-same-level.stderr | 10 ++++++++ src/test/ui/check-cfg/allow-top-level.rs | 15 ++++++++++++ src/test/ui/check-cfg/allow-upper-level.rs | 12 ++++++++++ 14 files changed, 122 insertions(+), 26 deletions(-) create mode 100644 src/test/ui/check-cfg/allow-macro-cfg.rs create mode 100644 src/test/ui/check-cfg/allow-same-level.rs create mode 100644 src/test/ui/check-cfg/allow-same-level.stderr create mode 100644 src/test/ui/check-cfg/allow-top-level.rs create mode 100644 src/test/ui/check-cfg/allow-upper-level.rs diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 68b536da9f70f..846abce9d6a6e 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -1,8 +1,7 @@ //! Parsing and validation of builtin attributes use rustc_ast as ast; -use rustc_ast::node_id::CRATE_NODE_ID; -use rustc_ast::{Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem}; +use rustc_ast::{Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem, NodeId}; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability}; use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg}; @@ -436,7 +435,12 @@ pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option { } /// Tests if a cfg-pattern matches the cfg set -pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool { +pub fn cfg_matches( + cfg: &ast::MetaItem, + sess: &ParseSess, + lint_node_id: NodeId, + features: Option<&Features>, +) -> bool { eval_condition(cfg, sess, features, &mut |cfg| { try_gate_cfg(cfg, sess, features); let error = |span, msg| { @@ -470,7 +474,7 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat sess.buffer_lint_with_diagnostic( UNEXPECTED_CFGS, cfg.span, - CRATE_NODE_ID, + lint_node_id, "unexpected `cfg` condition name", BuiltinLintDiagnostics::UnexpectedCfg(ident.span, name, None), ); @@ -482,7 +486,7 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat sess.buffer_lint_with_diagnostic( UNEXPECTED_CFGS, cfg.span, - CRATE_NODE_ID, + lint_node_id, "unexpected `cfg` condition value", BuiltinLintDiagnostics::UnexpectedCfg( cfg.name_value_literal_span().unwrap(), diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 1e1cf917c6093..f5ef4765df64f 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -19,7 +19,12 @@ pub fn expand_cfg( match parse_cfg(cx, sp, tts) { Ok(cfg) => { - let matches_cfg = attr::cfg_matches(&cfg, &cx.sess.parse_sess, cx.ecfg.features); + let matches_cfg = attr::cfg_matches( + &cfg, + &cx.sess.parse_sess, + cx.current_expansion.lint_node_id, + cx.ecfg.features, + ); MacEager::expr(cx.expr_bool(sp, matches_cfg)) } Err(mut err) => { diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 31086a2acf8cc..6e7b74d835a90 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -5,6 +5,7 @@ use rustc_ast::mut_visit::MutVisitor; use rustc_ast::ptr::P; use rustc_ast::tokenstream::CanSynthesizeMissingTokens; use rustc_ast::visit::Visitor; +use rustc_ast::NodeId; use rustc_ast::{mut_visit, visit}; use rustc_ast::{AstLike, Attribute}; use rustc_expand::base::{Annotatable, ExtCtxt}; @@ -26,15 +27,16 @@ crate fn expand( ) -> Vec { check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); warn_on_duplicate_attribute(&ecx, &annotatable, sym::cfg_eval); - vec![cfg_eval(ecx.sess, ecx.ecfg.features, annotatable)] + vec![cfg_eval(ecx.sess, ecx.ecfg.features, annotatable, ecx.current_expansion.lint_node_id)] } crate fn cfg_eval( sess: &Session, features: Option<&Features>, annotatable: Annotatable, + lint_node_id: NodeId, ) -> Annotatable { - CfgEval { cfg: &mut StripUnconfigured { sess, features, config_tokens: true } } + CfgEval { cfg: &mut StripUnconfigured { sess, features, config_tokens: true, lint_node_id } } .configure_annotatable(annotatable) // Since the item itself has already been configured by the `InvocationCollector`, // we know that fold result vector will contain exactly one element. diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 47d7b6c259e33..61681ec66a48d 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -64,7 +64,12 @@ impl MultiItemModifier for Expander { match &mut resolutions[..] { [] => {} [(_, first_item, _), others @ ..] => { - *first_item = cfg_eval(sess, features, item.clone()); + *first_item = cfg_eval( + sess, + features, + item.clone(), + ecx.current_expansion.lint_node_id, + ); for (_, item, _) in others { *item = first_item.clone(); } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 587453fd8e8d6..9d4b189486ea2 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1,4 +1,5 @@ use rustc_arena::TypedArena; +use rustc_ast::CRATE_NODE_ID; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; @@ -2434,7 +2435,7 @@ fn add_upstream_native_libraries( fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { match lib.cfg { - Some(ref cfg) => rustc_attr::cfg_matches(cfg, &sess.parse_sess, None), + Some(ref cfg) => rustc_attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None), None => true, } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index c0d7bc359bf44..d43c6fec7d5ad 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -5,6 +5,7 @@ use rustc_ast::token::{DelimToken, Token, TokenKind}; use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; use rustc_ast::tokenstream::{DelimSpan, Spacing}; use rustc_ast::tokenstream::{LazyTokenStream, TokenTree}; +use rustc_ast::NodeId; use rustc_ast::{self as ast, AstLike, AttrStyle, Attribute, MetaItem}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; @@ -29,6 +30,7 @@ pub struct StripUnconfigured<'a> { /// This is only used for the input to derive macros, /// which needs eager expansion of `cfg` and `cfg_attr` pub config_tokens: bool, + pub lint_node_id: NodeId, } fn get_features( @@ -196,8 +198,13 @@ fn get_features( } // `cfg_attr`-process the crate's attributes and compute the crate's features. -pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) { - let mut strip_unconfigured = StripUnconfigured { sess, features: None, config_tokens: false }; +pub fn features( + sess: &Session, + mut krate: ast::Crate, + lint_node_id: NodeId, +) -> (ast::Crate, Features) { + let mut strip_unconfigured = + StripUnconfigured { sess, features: None, config_tokens: false, lint_node_id }; let unconfigured_attrs = krate.attrs.clone(); let diag = &sess.parse_sess.span_diagnostic; @@ -353,7 +360,12 @@ impl<'a> StripUnconfigured<'a> { ); } - if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) { + if !attr::cfg_matches( + &cfg_predicate, + &self.sess.parse_sess, + self.lint_node_id, + self.features, + ) { return vec![]; } @@ -445,7 +457,7 @@ impl<'a> StripUnconfigured<'a> { } }; parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { - attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features) + attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.lint_node_id, self.features) }) } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index bdc9c064a6f9c..36fb14a403e9b 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -551,11 +551,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // attribute is expanded. Therefore, we don't need to configure the tokens // Derive macros *can* see the results of cfg-expansion - they are handled // specially in `fully_expand_fragment` - cfg: StripUnconfigured { - sess: &self.cx.sess, - features: self.cx.ecfg.features, - config_tokens: false, - }, cx: self.cx, invocations: Vec::new(), monotonic: self.monotonic, @@ -1537,12 +1532,20 @@ impl InvocationCollectorNode for AstLikeWrapper, OptExprTag> { struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, - cfg: StripUnconfigured<'a>, invocations: Vec<(Invocation, Option>)>, monotonic: bool, } impl<'a, 'b> InvocationCollector<'a, 'b> { + fn cfg(&self) -> StripUnconfigured<'_> { + StripUnconfigured { + sess: &self.cx.sess, + features: self.cx.ecfg.features, + config_tokens: false, + lint_node_id: self.cx.current_expansion.lint_node_id, + } + } + fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment { let expn_id = LocalExpnId::fresh_empty(); let vis = kind.placeholder_visibility(); @@ -1682,7 +1685,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { attr: ast::Attribute, pos: usize, ) -> bool { - let res = self.cfg.cfg_true(&attr); + let res = self.cfg().cfg_true(&attr); if res { // FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion, // and some tools like rustdoc and clippy rely on that. Find a way to remove them @@ -1695,7 +1698,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn expand_cfg_attr(&self, node: &mut impl AstLike, attr: ast::Attribute, pos: usize) { node.visit_attrs(|attrs| { - attrs.splice(pos..pos, self.cfg.expand_cfg_attr(attr, false)); + attrs.splice(pos..pos, self.cfg().expand_cfg_attr(attr, false)); }); } @@ -1717,7 +1720,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { continue; } _ => { - Node::pre_flat_map_node_collect_attr(&self.cfg, &attr); + Node::pre_flat_map_node_collect_attr(&self.cfg(), &attr); self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND) .make_ast::() } @@ -1881,7 +1884,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { fn visit_expr(&mut self, node: &mut P) { // FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`. if let Some(attr) = node.attrs.first() { - self.cfg.maybe_emit_expr_attr_err(attr); + self.cfg().maybe_emit_expr_attr_err(attr); } self.visit_node(node) } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index c0552fd200be8..951ae0ce2a4c7 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -2,6 +2,7 @@ use crate::interface::{Compiler, Result}; use crate::proc_macro_decls; use crate::util; +use ast::CRATE_NODE_ID; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::{self as ast, visit}; use rustc_borrowck as mir_borrowck; @@ -188,7 +189,7 @@ pub fn register_plugins<'a>( ) }); - let (krate, features) = rustc_expand::config::features(sess, krate); + let (krate, features) = rustc_expand::config::features(sess, krate, CRATE_NODE_ID); // these need to be set "early" so that expansion sees `quote` if enabled. sess.init_features(features); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index dce1b35c6b889..7cdcb6a4ab302 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -1,3 +1,4 @@ +use rustc_ast::CRATE_NODE_ID; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; @@ -21,7 +22,7 @@ crate fn collect(tcx: TyCtxt<'_>) -> Vec { crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { match lib.cfg { - Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None), + Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None), None => true, } } diff --git a/src/test/ui/check-cfg/allow-macro-cfg.rs b/src/test/ui/check-cfg/allow-macro-cfg.rs new file mode 100644 index 0000000000000..8016a4d190cc3 --- /dev/null +++ b/src/test/ui/check-cfg/allow-macro-cfg.rs @@ -0,0 +1,14 @@ +// This test check that local #[allow(unexpected_cfgs)] works +// +// check-pass +// compile-flags:--check-cfg=names() -Z unstable-options + +#[allow(unexpected_cfgs)] +fn foo() { + if cfg!(FALSE) {} +} + +fn main() { + #[allow(unexpected_cfgs)] + if cfg!(FALSE) {} +} diff --git a/src/test/ui/check-cfg/allow-same-level.rs b/src/test/ui/check-cfg/allow-same-level.rs new file mode 100644 index 0000000000000..6c869dc420235 --- /dev/null +++ b/src/test/ui/check-cfg/allow-same-level.rs @@ -0,0 +1,11 @@ +// This test check that #[allow(unexpected_cfgs)] doesn't work if put on the same level +// +// check-pass +// compile-flags:--check-cfg=names() -Z unstable-options + +#[allow(unexpected_cfgs)] +#[cfg(FALSE)] +//~^ WARNING unexpected `cfg` condition name +fn bar() {} + +fn main() {} diff --git a/src/test/ui/check-cfg/allow-same-level.stderr b/src/test/ui/check-cfg/allow-same-level.stderr new file mode 100644 index 0000000000000..7797de584b9e1 --- /dev/null +++ b/src/test/ui/check-cfg/allow-same-level.stderr @@ -0,0 +1,10 @@ +warning: unexpected `cfg` condition name + --> $DIR/allow-same-level.rs:7:7 + | +LL | #[cfg(FALSE)] + | ^^^^^ + | + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/check-cfg/allow-top-level.rs b/src/test/ui/check-cfg/allow-top-level.rs new file mode 100644 index 0000000000000..d14b0eae5ccdd --- /dev/null +++ b/src/test/ui/check-cfg/allow-top-level.rs @@ -0,0 +1,15 @@ +// This test check that a top-level #![allow(unexpected_cfgs)] works +// +// check-pass +// compile-flags:--check-cfg=names() -Z unstable-options + +#![allow(unexpected_cfgs)] + +#[cfg(FALSE)] +fn bar() {} + +fn foo() { + if cfg!(FALSE) {} +} + +fn main() {} diff --git a/src/test/ui/check-cfg/allow-upper-level.rs b/src/test/ui/check-cfg/allow-upper-level.rs new file mode 100644 index 0000000000000..04340694d9c1e --- /dev/null +++ b/src/test/ui/check-cfg/allow-upper-level.rs @@ -0,0 +1,12 @@ +// This test check that #[allow(unexpected_cfgs)] work if put on an upper level +// +// check-pass +// compile-flags:--check-cfg=names() -Z unstable-options + +#[allow(unexpected_cfgs)] +mod aa { + #[cfg(FALSE)] + fn bar() {} +} + +fn main() {} From 400d343796065e57e78d92b4cf74c78bec38452a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 23:34:25 +0300 Subject: [PATCH 05/16] Suggest adding `Copy` bound when Adt is moved out Previously we've only suggested adding `Copy` bounds when the type being moved/copied is a type parameter (generic). With this commit we also suggest adding bounds when a type - Can be copy - All predicates that need to be satisfied for that are based on type params i.e. we will suggest `T: Copy` for `Option`, but won't suggest anything for `Option`. Future work: it would be nice to also suggest adding `.clone()` calls --- .../src/diagnostics/conflict_errors.rs | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index eb906d5fde7b4..e8ecb3cb6f3e7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -5,16 +5,21 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; use rustc_middle::mir::{ self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; +use rustc_middle::ty::{ + self, suggest_constraining_type_param, suggest_constraining_type_params, PredicateKind, Ty, +}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::symbol::sym; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::TraitEngineExt as _; use crate::borrow_set::TwoPhaseActivation; use crate::borrowck_errors; @@ -423,7 +428,63 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None, ); } + } else { + // Try to find predicates on *generic params* that would allow copying `ty` + + let tcx = self.infcx.tcx; + let generics = tcx.generics_of(self.mir_def_id()); + if let Some(hir_generics) = tcx + .typeck_root_def_id(self.mir_def_id().to_def_id()) + .as_local() + .and_then(|def_id| tcx.hir().get_generics(def_id)) + { + let predicates: Result, _> = tcx.infer_ctxt().enter(|infcx| { + let mut fulfill_cx = + >::new(infcx.tcx); + + let copy_did = infcx.tcx.lang_items().copy_trait().unwrap(); + let cause = ObligationCause::new( + span, + self.mir_hir_id(), + rustc_infer::traits::ObligationCauseCode::MiscObligation, + ); + fulfill_cx.register_bound(&infcx, self.param_env, ty, copy_did, cause); + let errors = fulfill_cx.select_where_possible(&infcx); + + // Only emit suggestion if all required predicates are on generic + errors + .into_iter() + .map(|err| match err.obligation.predicate.kind().skip_binder() { + PredicateKind::Trait(predicate) => { + match predicate.self_ty().kind() { + ty::Param(param_ty) => Ok(( + generics.type_param(param_ty, tcx), + predicate + .trait_ref + .print_only_trait_path() + .to_string(), + )), + _ => Err(()), + } + } + _ => Err(()), + }) + .collect() + }); + + if let Ok(predicates) = predicates { + suggest_constraining_type_params( + tcx, + hir_generics, + &mut err, + predicates.iter().map(|(param, constraint)| { + (param.name.as_str(), &**constraint, None) + }), + ); + } + } } + let span = if let Some(local) = place.as_local() { let decl = &self.body.local_decls[local]; Some(decl.source_info.span) From 879efa84516da086e7461d65fff6797c8ae1cff9 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 23:38:00 +0300 Subject: [PATCH 06/16] Add a test for Adt copy suggestions --- .../use_of_moved_value_copy_suggestions.rs | 65 ++++++++ ...use_of_moved_value_copy_suggestions.stderr | 151 ++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 src/test/ui/moves/use_of_moved_value_copy_suggestions.rs create mode 100644 src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs new file mode 100644 index 0000000000000..0930927c49d66 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs @@ -0,0 +1,65 @@ +fn duplicate_t(t: T) -> (T, T) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_opt(t: Option) -> (Option, Option) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom(t: S) -> (S, S) { + (t, t) //~ use of moved value: `t` +} + +struct S(T); +trait Trait {} +impl Clone for S { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +impl Copy for S {} + +trait A {} +trait B {} + +// Test where bounds are added with different bound placements +fn duplicate_custom_1(t: S) -> (S, S) where { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_2(t: S) -> (S, S) +where + T: A, +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_3(t: S) -> (S, S) +where + T: A, + T: B, +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_4(t: S) -> (S, S) +where + T: B, +{ + (t, t) //~ use of moved value: `t` +} + +// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint +fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + (t, t) //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr new file mode 100644 index 0000000000000..c32ac2cd78aa0 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr @@ -0,0 +1,151 @@ +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:2:9 + | +LL | fn duplicate_t(t: T) -> (T, T) { + | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_t(t: T) -> (T, T) { + | ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9 + | +LL | fn duplicate_opt(t: Option) -> (Option, Option) { + | - move occurs because `t` has type `Option`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_opt(t: Option) -> (Option, Option) { + | ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:10:9 + | +LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + | - move occurs because `t` has type `(T,)`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + | ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:14:9 + | +LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + | - move occurs because `t` has type `(A, B)`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameters + | +LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + | ++++++ ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:18:9 + | +LL | fn duplicate_custom(t: S) -> (S, S) { + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_custom(t: S) -> (S, S) { + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:35:9 + | +LL | fn duplicate_custom_1(t: S) -> (S, S) where { + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_custom_1(t: S) -> (S, S) where { + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:42:9 + | +LL | fn duplicate_custom_2(t: S) -> (S, S) + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +... +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider further restricting this bound + | +LL | T: A + Trait + Copy, + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:50:9 + | +LL | fn duplicate_custom_3(t: S) -> (S, S) + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +... +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider further restricting type parameter `T` + | +LL | T: B, T: Trait, T: Copy + | ~~~~~~~~~~~~~~~~~~~ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:57:9 + | +LL | fn duplicate_custom_4(t: S) -> (S, S) + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +... +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider further restricting this bound + | +LL | T: B + Trait + Copy, + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:62:9 + | +LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0382`. From f0a16b856011c8e63fac98fd6c127c2a0bbd532e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 1 Mar 2022 14:11:42 +0300 Subject: [PATCH 07/16] Use rustfix in copy suggestion test --- .../use_of_moved_value_clone_suggestions.rs | 6 ++ ...se_of_moved_value_clone_suggestions.stderr | 13 ++++ .../use_of_moved_value_copy_suggestions.fixed | 72 +++++++++++++++++++ .../use_of_moved_value_copy_suggestions.rs | 17 +++-- ...use_of_moved_value_copy_suggestions.stderr | 36 +++++----- 5 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 src/test/ui/moves/use_of_moved_value_clone_suggestions.rs create mode 100644 src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr create mode 100644 src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs new file mode 100644 index 0000000000000..d5c8d4e6bdf2b --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs @@ -0,0 +1,6 @@ +// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint +fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + (t, t) //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr new file mode 100644 index 0000000000000..c25981e6f8063 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_clone_suggestions.rs:3:9 + | +LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed new file mode 100644 index 0000000000000..d31046c77006e --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed @@ -0,0 +1,72 @@ +// run-rustfix +#![allow(dead_code)] + +fn duplicate_t(t: T) -> (T, T) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_opt(t: Option) -> (Option, Option) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + //~^ HELP consider restricting type parameters + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom(t: S) -> (S, S) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +struct S(T); +trait Trait {} +impl Clone for S { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +impl Copy for S {} + +trait A {} +trait B {} + +// Test where bounds are added with different bound placements +fn duplicate_custom_1(t: S) -> (S, S) where { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_2(t: S) -> (S, S) +where + T: A + Trait + Copy, + //~^ HELP consider further restricting this bound +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_3(t: S) -> (S, S) +where + T: A, + T: B, T: Trait, T: Copy + //~^ HELP consider further restricting type parameter `T` +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_4(t: S) -> (S, S) +where + T: B + Trait + Copy, + //~^ HELP consider further restricting this bound +{ + (t, t) //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs index 0930927c49d66..7cc5189fac017 100644 --- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs @@ -1,20 +1,28 @@ +// run-rustfix +#![allow(dead_code)] + fn duplicate_t(t: T) -> (T, T) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_opt(t: Option) -> (Option, Option) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + //~^ HELP consider restricting type parameters (t, t) //~ use of moved value: `t` } fn duplicate_custom(t: S) -> (S, S) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } @@ -32,12 +40,14 @@ trait B {} // Test where bounds are added with different bound placements fn duplicate_custom_1(t: S) -> (S, S) where { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_custom_2(t: S) -> (S, S) where T: A, + //~^ HELP consider further restricting this bound { (t, t) //~ use of moved value: `t` } @@ -46,6 +56,7 @@ fn duplicate_custom_3(t: S) -> (S, S) where T: A, T: B, + //~^ HELP consider further restricting type parameter `T` { (t, t) //~ use of moved value: `t` } @@ -53,13 +64,9 @@ where fn duplicate_custom_4(t: S) -> (S, S) where T: B, + //~^ HELP consider further restricting this bound { (t, t) //~ use of moved value: `t` } -// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint -fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - (t, t) //~ use of moved value: `t` -} - fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr index c32ac2cd78aa0..8e72697ca30bb 100644 --- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr @@ -1,8 +1,9 @@ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:2:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9 | LL | fn duplicate_t(t: T) -> (T, T) { | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -14,10 +15,11 @@ LL | fn duplicate_t(t: T) -> (T, T) { | ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:11:9 | LL | fn duplicate_opt(t: Option) -> (Option, Option) { | - move occurs because `t` has type `Option`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -29,10 +31,11 @@ LL | fn duplicate_opt(t: Option) -> (Option, Option) { | ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:10:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:16:9 | LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { | - move occurs because `t` has type `(T,)`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -44,10 +47,11 @@ LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { | ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:14:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:21:9 | LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { | - move occurs because `t` has type `(A, B)`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -59,10 +63,11 @@ LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { | ++++++ ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:18:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:26:9 | LL | fn duplicate_custom(t: S) -> (S, S) { | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -74,10 +79,11 @@ LL | fn duplicate_custom(t: S) -> (S, S) { | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:35:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:44:9 | LL | fn duplicate_custom_1(t: S) -> (S, S) where { | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -89,7 +95,7 @@ LL | fn duplicate_custom_1(t: S) -> (S, S) where { | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:42:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:52:9 | LL | fn duplicate_custom_2(t: S) -> (S, S) | - move occurs because `t` has type `S`, which does not implement the `Copy` trait @@ -105,7 +111,7 @@ LL | T: A + Trait + Copy, | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:50:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:61:9 | LL | fn duplicate_custom_3(t: S) -> (S, S) | - move occurs because `t` has type `S`, which does not implement the `Copy` trait @@ -121,7 +127,7 @@ LL | T: B, T: Trait, T: Copy | ~~~~~~~~~~~~~~~~~~~ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:57:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:69:9 | LL | fn duplicate_custom_4(t: S) -> (S, S) | - move occurs because `t` has type `S`, which does not implement the `Copy` trait @@ -136,16 +142,6 @@ help: consider further restricting this bound LL | T: B + Trait + Copy, | ++++++++++++++ -error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:62:9 - | -LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait -LL | (t, t) - | - ^ value used here after move - | | - | value moved here - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0382`. From 723d33462c79a3628ca4cf357044b3d5809e7a96 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 1 Mar 2022 11:42:10 -0800 Subject: [PATCH 08/16] Restore the local filter on mono item sorting In `CodegenUnit::items_in_deterministic_order`, there's a comment that only local HirIds should be taken into account, but #90408 removed the `as_local` call that sets others to None. Restoring that check fixes the s390x hangs seen in [RHBZ 2058803]. [RHBZ 2058803]: https://bugzilla.redhat.com/show_bug.cgi?id=2058803 --- compiler/rustc_middle/src/mir/mono.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 892808386deef..13c325a14e402 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -7,6 +7,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::ItemId; +use rustc_index::vec::Idx; use rustc_query_system::ich::{NodeIdHashingMode, StableHashingContext}; use rustc_session::config::OptLevel; use rustc_span::source_map::Span; @@ -380,7 +381,7 @@ impl<'tcx> CodegenUnit<'tcx> { // instances into account. The others don't matter for // the codegen tests and can even make item order // unstable. - InstanceDef::Item(def) => Some(def.did.index.as_usize()), + InstanceDef::Item(def) => def.did.as_local().map(Idx::index), InstanceDef::VtableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) @@ -391,10 +392,8 @@ impl<'tcx> CodegenUnit<'tcx> { | InstanceDef::CloneShim(..) => None, } } - MonoItem::Static(def_id) => Some(def_id.index.as_usize()), - MonoItem::GlobalAsm(item_id) => { - Some(item_id.def_id.to_def_id().index.as_usize()) - } + MonoItem::Static(def_id) => def_id.as_local().map(Idx::index), + MonoItem::GlobalAsm(item_id) => Some(item_id.def_id.index()), }, item.symbol_name(tcx), ) From f28786643e6ce7ec3104f96cd40a0baa8f8d3cac Mon Sep 17 00:00:00 2001 From: Edwin Amsler Date: Tue, 1 Mar 2022 15:47:11 -0600 Subject: [PATCH 09/16] Demote Windows XP to no_std only Modify the tier 3 non-ARM targets to show the standard library will no longer build for these and there is no work being done to change that. --- src/doc/rustc/src/platform-support.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d7e0b9d50f084..4c05818440b09 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -139,7 +139,7 @@ target | std | notes `armv7r-none-eabi` | * | Bare ARMv7-R `armv7r-none-eabihf` | * | Bare ARMv7-R, hardfloat `asmjs-unknown-emscripten` | ✓ | asm.js via Emscripten -`i586-pc-windows-msvc` | ✓ | 32-bit Windows w/o SSE +`i586-pc-windows-msvc` | * | 32-bit Windows w/o SSE `i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 4.4, glibc 2.23) `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, MUSL `i686-linux-android` | ✓ | 32-bit x86 Android @@ -236,7 +236,7 @@ target | std | host | notes `hexagon-unknown-linux-musl` | ? | | `i386-apple-ios` | ✓ | | 32-bit x86 iOS `i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.7+, Lion+) -`i686-pc-windows-msvc` | ✓ | | 32-bit Windows XP support +`i686-pc-windows-msvc` | * | | 32-bit Windows XP support `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku `i686-unknown-netbsd` | ✓ | ✓ | NetBSD/i386 with SSE2 [`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD @@ -283,7 +283,7 @@ target | std | host | notes [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly `x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 `x86_64-apple-tvos` | * | | x86 64-bit tvOS -`x86_64-pc-windows-msvc` | ✓ | | 64-bit Windows XP support +`x86_64-pc-windows-msvc` | * | | 64-bit Windows XP support `x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku From 6a838e41bb9d8c24a621cb69efab4c99948fdcf1 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 1 Mar 2022 15:53:22 -0800 Subject: [PATCH 10/16] Use CHECK-DAG in codegen/debuginfo-generic-closure-env-names.rs --- .../debuginfo-generic-closure-env-names.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test/codegen/debuginfo-generic-closure-env-names.rs b/src/test/codegen/debuginfo-generic-closure-env-names.rs index ad59f740b567f..b29f8b4a029d0 100644 --- a/src/test/codegen/debuginfo-generic-closure-env-names.rs +++ b/src/test/codegen/debuginfo-generic-closure-env-names.rs @@ -3,7 +3,7 @@ // of the enclosing functions don't get lost. // // Unfortunately, the order that debuginfo gets emitted into LLVM IR becomes a bit hard -// to predict once async fns are involved. +// to predict once async fns are involved, so DAG allows any order. // // Note that the test does not check async-fns when targeting MSVC because debuginfo for // those does not follow the enum-fallback encoding yet and thus is incomplete. @@ -27,24 +27,24 @@ // CHECK: ![[generic_async_block_NAMESPACE:[0-9]+]] = !DINamespace(name: "generic_async_block" // function_containing_closure() -// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[function_containing_closure_NAMESPACE]] -// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: ![[function_containing_closure_NAMESPACE]] +// NONMSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[function_containing_closure_NAMESPACE]] +// MSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: ![[function_containing_closure_NAMESPACE]] // generic_async_function() -// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: ![[generic_async_function_NAMESPACE]] +// NONMSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: ![[generic_async_function_NAMESPACE]] // generic_async_function() -// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: ![[generic_async_function_NAMESPACE]] +// NONMSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: ![[generic_async_function_NAMESPACE]] // generic_async_block() -// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}", scope: ![[generic_async_block_NAMESPACE]] +// NONMSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}", scope: ![[generic_async_block_NAMESPACE]] // generic_async_block() -// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}", scope: ![[generic_async_block_NAMESPACE]] +// NONMSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}", scope: ![[generic_async_block_NAMESPACE]] // function_containing_closure() -// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[function_containing_closure_NAMESPACE]] -// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: ![[function_containing_closure_NAMESPACE]] +// NONMSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[function_containing_closure_NAMESPACE]] +// MSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: ![[function_containing_closure_NAMESPACE]] #![crate_type = "lib"] From 6739299d18a16851140bc6b24b8eeae31e3d5878 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Mar 2022 20:02:59 -0500 Subject: [PATCH 11/16] Miri/CTFE: properly treat overflow in (signed) division/rem as UB --- .../src/interpret/intrinsics.rs | 12 ++------ .../src/interpret/operator.rs | 18 +++++++----- .../rustc_middle/src/mir/interpret/error.rs | 6 ++++ .../rustc_mir_transform/src/const_prop.rs | 11 +++++++- library/core/tests/num/wrapping.rs | 10 +++++++ src/test/ui/consts/const-int-unchecked.stderr | 4 +-- .../issue-8460-const.noopt.stderr | 28 +++++++++---------- .../issue-8460-const.opt.stderr | 28 +++++++++---------- ...8460-const.opt_with_overflow_checks.stderr | 28 +++++++++---------- .../ui/numbers-arithmetic/issue-8460-const.rs | 24 ++++++++-------- 10 files changed, 93 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d6f856a6f0a70..bf7e811c76f8e 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -500,15 +500,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`. // First, check x % y != 0 (or if that computation overflows). let (res, overflow, _ty) = self.overflowing_binary_op(BinOp::Rem, &a, &b)?; - if overflow || res.assert_bits(a.layout.size) != 0 { - // Then, check if `b` is -1, which is the "MIN / -1" case. - let minus1 = Scalar::from_int(-1, dest.layout.size); - let b_scalar = b.to_scalar().unwrap(); - if b_scalar == minus1 { - throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented") - } else { - throw_ub_format!("exact_div: {} cannot be divided by {} without remainder", a, b,) - } + assert!(!overflow); // All overflow is UB, so this should never return on overflow. + if res.assert_bits(a.layout.size) != 0 { + throw_ub_format!("exact_div: {} cannot be divided by {} without remainder", a, b) } // `Rem` says this is all right, so we can let `Div` do its job. self.binop_ignore_overflow(BinOp::Div, &a, &b, dest) diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 48c90e1881a9a..079ce9f07b8e1 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -196,16 +196,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => None, }; if let Some(op) = op { + let l = self.sign_extend(l, left_layout) as i128; let r = self.sign_extend(r, right_layout) as i128; - // We need a special check for overflowing remainder: - // "int_min % -1" overflows and returns 0, but after casting things to a larger int - // type it does *not* overflow nor give an unrepresentable result! - if bin_op == Rem { - if r == -1 && l == (1 << (size.bits() - 1)) { - return Ok((Scalar::from_int(0, size), true, left_layout.ty)); + + // We need a special check for overflowing Rem and Div since they are *UB* + // on overflow, which can happen with "int_min $OP -1". + if matches!(bin_op, Rem | Div) { + if l == size.signed_int_min() && r == -1 { + if bin_op == Rem { + throw_ub!(RemainderOverflow) + } else { + throw_ub!(DivisionOverflow) + } } } - let l = self.sign_extend(l, left_layout) as i128; let (result, oflo) = op(l, r); // This may be out-of-bounds for the result type, so we have to truncate ourselves. diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 31468ce73bfc5..389d31ea0c472 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -233,6 +233,10 @@ pub enum UndefinedBehaviorInfo<'tcx> { DivisionByZero, /// Something was "remainded" by 0 (x % 0). RemainderByZero, + /// Signed division overflowed (INT_MIN / -1). + DivisionOverflow, + /// Signed remainder overflowed (INT_MIN % -1). + RemainderOverflow, /// Overflowing inbounds pointer arithmetic. PointerArithOverflow, /// Invalid metadata in a wide pointer (using `str` to avoid allocations). @@ -310,6 +314,8 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { } DivisionByZero => write!(f, "dividing by zero"), RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), + DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"), + RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"), PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), InvalidVtableDropFn(sig) => write!( diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 6075f572a651c..b724b527c7dfb 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -1200,12 +1200,21 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { AssertKind::RemainderByZero(op) => { Some(AssertKind::RemainderByZero(eval_to_int(op))) } + AssertKind::Overflow(bin_op @ (BinOp::Div | BinOp::Rem), op1, op2) => { + // Division overflow is *UB* in the MIR, and different than the + // other overflow checks. + Some(AssertKind::Overflow( + *bin_op, + eval_to_int(op1), + eval_to_int(op2), + )) + } AssertKind::BoundsCheck { ref len, ref index } => { let len = eval_to_int(len); let index = eval_to_int(index); Some(AssertKind::BoundsCheck { len, index }) } - // Overflow is are already covered by checks on the binary operators. + // Remaining overflow errors are already covered by checks on the binary operators. AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => None, // Need proper const propagator for these. _ => None, diff --git a/library/core/tests/num/wrapping.rs b/library/core/tests/num/wrapping.rs index c4fb321ef26cf..8ded139a1809f 100644 --- a/library/core/tests/num/wrapping.rs +++ b/library/core/tests/num/wrapping.rs @@ -308,3 +308,13 @@ fn wrapping_int_api() { } } } + +#[test] +fn wrapping_const() { + // Specifically the wrapping behavior of division and remainder is subtle, + // see https://github.com/rust-lang/rust/pull/94512. + const _: () = { + assert!(i32::MIN.wrapping_div(-1) == i32::MIN); + assert!(i32::MIN.wrapping_rem(-1) == 0); + }; +} diff --git a/src/test/ui/consts/const-int-unchecked.stderr b/src/test/ui/consts/const-int-unchecked.stderr index 22e8c8dabc970..ad880d56d904d 100644 --- a/src/test/ui/consts/const-int-unchecked.stderr +++ b/src/test/ui/consts/const-int-unchecked.stderr @@ -266,7 +266,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:134:25 | LL | const _: i32 = unsafe { std::intrinsics::unchecked_div(i32::MIN, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_div` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow in signed division (dividing MIN by -1) error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:137:25 @@ -278,7 +278,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:139:25 | LL | const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::MIN, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_rem` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow in signed remainder (dividing MIN by -1) error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:144:25 diff --git a/src/test/ui/numbers-arithmetic/issue-8460-const.noopt.stderr b/src/test/ui/numbers-arithmetic/issue-8460-const.noopt.stderr index d94c7742de301..e2eee1ccdc98c 100644 --- a/src/test/ui/numbers-arithmetic/issue-8460-const.noopt.stderr +++ b/src/test/ui/numbers-arithmetic/issue-8460-const.noopt.stderr @@ -1,36 +1,36 @@ -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:13:36 | LL | assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute `isize::MIN / -1_isize`, which would overflow | - = note: `#[deny(arithmetic_overflow)]` on by default + = note: `#[deny(unconditional_panic)]` on by default -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:15:36 | LL | assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute `i8::MIN / -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:17:36 | LL | assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i16::MIN / -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:19:36 | LL | assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i32::MIN / -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:21:36 | LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i64::MIN / -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:23:36 | LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); @@ -41,8 +41,6 @@ error: this operation will panic at runtime | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ attempt to divide `1_isize` by zero - | - = note: `#[deny(unconditional_panic)]` on by default error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:27:36 @@ -74,37 +72,37 @@ error: this operation will panic at runtime LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); | ^^^^^^^^^ attempt to divide `1_i128` by zero -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:37:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `isize::MIN % -1_isize`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:39:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute the remainder of `i8::MIN % -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:41:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i16::MIN % -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:45:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:47:36 | LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); diff --git a/src/test/ui/numbers-arithmetic/issue-8460-const.opt.stderr b/src/test/ui/numbers-arithmetic/issue-8460-const.opt.stderr index d94c7742de301..e2eee1ccdc98c 100644 --- a/src/test/ui/numbers-arithmetic/issue-8460-const.opt.stderr +++ b/src/test/ui/numbers-arithmetic/issue-8460-const.opt.stderr @@ -1,36 +1,36 @@ -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:13:36 | LL | assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute `isize::MIN / -1_isize`, which would overflow | - = note: `#[deny(arithmetic_overflow)]` on by default + = note: `#[deny(unconditional_panic)]` on by default -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:15:36 | LL | assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute `i8::MIN / -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:17:36 | LL | assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i16::MIN / -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:19:36 | LL | assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i32::MIN / -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:21:36 | LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i64::MIN / -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:23:36 | LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); @@ -41,8 +41,6 @@ error: this operation will panic at runtime | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ attempt to divide `1_isize` by zero - | - = note: `#[deny(unconditional_panic)]` on by default error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:27:36 @@ -74,37 +72,37 @@ error: this operation will panic at runtime LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); | ^^^^^^^^^ attempt to divide `1_i128` by zero -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:37:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `isize::MIN % -1_isize`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:39:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute the remainder of `i8::MIN % -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:41:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i16::MIN % -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:45:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:47:36 | LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); diff --git a/src/test/ui/numbers-arithmetic/issue-8460-const.opt_with_overflow_checks.stderr b/src/test/ui/numbers-arithmetic/issue-8460-const.opt_with_overflow_checks.stderr index d94c7742de301..e2eee1ccdc98c 100644 --- a/src/test/ui/numbers-arithmetic/issue-8460-const.opt_with_overflow_checks.stderr +++ b/src/test/ui/numbers-arithmetic/issue-8460-const.opt_with_overflow_checks.stderr @@ -1,36 +1,36 @@ -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:13:36 | LL | assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute `isize::MIN / -1_isize`, which would overflow | - = note: `#[deny(arithmetic_overflow)]` on by default + = note: `#[deny(unconditional_panic)]` on by default -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:15:36 | LL | assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute `i8::MIN / -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:17:36 | LL | assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i16::MIN / -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:19:36 | LL | assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i32::MIN / -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:21:36 | LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i64::MIN / -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:23:36 | LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); @@ -41,8 +41,6 @@ error: this operation will panic at runtime | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ attempt to divide `1_isize` by zero - | - = note: `#[deny(unconditional_panic)]` on by default error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:27:36 @@ -74,37 +72,37 @@ error: this operation will panic at runtime LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); | ^^^^^^^^^ attempt to divide `1_i128` by zero -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:37:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `isize::MIN % -1_isize`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:39:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute the remainder of `i8::MIN % -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:41:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i16::MIN % -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:45:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:47:36 | LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); diff --git a/src/test/ui/numbers-arithmetic/issue-8460-const.rs b/src/test/ui/numbers-arithmetic/issue-8460-const.rs index dc754666c8e1f..8cad6deb3db5e 100644 --- a/src/test/ui/numbers-arithmetic/issue-8460-const.rs +++ b/src/test/ui/numbers-arithmetic/issue-8460-const.rs @@ -11,17 +11,17 @@ use std::thread; fn main() { assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); //~^ ERROR operation will panic assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); @@ -35,17 +35,17 @@ fn main() { assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); //~^ ERROR operation will panic assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); //~^ ERROR operation will panic assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); From 3768f0b813cd6944b0893b76bc0add776eb8cae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Wed, 2 Mar 2022 10:33:50 +0000 Subject: [PATCH 12/16] update char signess for openbsd adds more archs for openbsd: arm, mips64, powerpc, powerpc64, and riscv64. --- library/core/src/ffi/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index e5255686ff984..81f04994c6f8d 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -130,7 +130,16 @@ mod c_char_definition { target_os = "netbsd", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") ), - all(target_os = "openbsd", target_arch = "aarch64"), + all( + target_os = "openbsd", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "riscv64", + ), + ), all( target_os = "vxworks", any( From fa8e1bedd34d85d4cbe83b8c61a53d080a83e72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Wed, 2 Mar 2022 12:12:28 +0000 Subject: [PATCH 13/16] merge the char signess list of archs with freebsd as it is the same --- library/core/src/ffi/mod.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 81f04994c6f8d..2b611e5aae276 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -117,7 +117,7 @@ mod c_char_definition { all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), all(target_os = "l4re", target_arch = "x86_64"), all( - target_os = "freebsd", + any(target_os = "freebsd", target_os = "openbsd"), any( target_arch = "aarch64", target_arch = "arm", @@ -130,16 +130,6 @@ mod c_char_definition { target_os = "netbsd", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") ), - all( - target_os = "openbsd", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "riscv64", - ), - ), all( target_os = "vxworks", any( From 003c892f9f5bdb8b19f27e75e9d89d148ce030e8 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 19 Jan 2022 13:11:26 +0100 Subject: [PATCH 14/16] Fix Ok(()) suggestion when desugaring is involved. --- compiler/rustc_typeck/src/check/demand.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 3b644099ecf04..501ff2e3850be 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -276,11 +276,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we suggest adding a separate return expression instead. // (To avoid things like suggesting `Ok(while .. { .. })`.) if expr_ty.is_unit() { + let mut id = expr.hir_id; + let mut parent; + + // Unroll desugaring, to make sure this works for `for` loops etc. + loop { + parent = self.tcx.hir().get_parent_node(id); + if let Some(parent_span) = self.tcx.hir().opt_span(parent) { + if parent_span.find_ancestor_inside(expr.span).is_some() { + // The parent node is part of the same span, so is the result of the + // same expansion/desugaring and not the 'real' parent node. + id = parent; + continue; + } + } + break; + } + if let Some(hir::Node::Block(&hir::Block { span: block_span, expr: Some(e), .. - })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) + })) = self.tcx.hir().find(parent) { - if e.hir_id == expr.hir_id { + if e.hir_id == id { if let Some(span) = expr.span.find_ancestor_inside(block_span) { let return_suggestions = if self.tcx.is_diagnostic_item(sym::Result, expected_adt.did) { From 85cc4710efe60ce52781a40fab82860c1b81070b Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 19 Jan 2022 13:11:48 +0100 Subject: [PATCH 15/16] Add more tests for mismatched Option/Result return types. --- .../ui/did_you_mean/compatible-variants.rs | 15 +++++ .../did_you_mean/compatible-variants.stderr | 61 ++++++++++++++++--- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/test/ui/did_you_mean/compatible-variants.rs b/src/test/ui/did_you_mean/compatible-variants.rs index a70dda8386f08..b078064b26745 100644 --- a/src/test/ui/did_you_mean/compatible-variants.rs +++ b/src/test/ui/did_you_mean/compatible-variants.rs @@ -23,6 +23,21 @@ fn b() -> Result<(), ()> { //~| HELP try adding an expression } +fn c() -> Option<()> { + for _ in [1, 2] { + //~^ ERROR mismatched types + f(); + } + //~^ HELP try adding an expression +} + +fn d() -> Option<()> { + c()? + //~^ ERROR incompatible types + //~| HELP try removing this `?` + //~| HELP try adding an expression +} + fn main() { let _: Option<()> = while false {}; //~^ ERROR mismatched types diff --git a/src/test/ui/did_you_mean/compatible-variants.stderr b/src/test/ui/did_you_mean/compatible-variants.stderr index 0dfd8f5c128f6..51c1bf97c4e2c 100644 --- a/src/test/ui/did_you_mean/compatible-variants.stderr +++ b/src/test/ui/did_you_mean/compatible-variants.stderr @@ -37,7 +37,52 @@ LL + Ok(()) | error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:27:25 + --> $DIR/compatible-variants.rs:27:5 + | +LL | fn c() -> Option<()> { + | ---------- expected `Option<()>` because of return type +LL | / for _ in [1, 2] { +LL | | +LL | | f(); +LL | | } + | |_____^ expected enum `Option`, found `()` + | + = note: expected enum `Option<()>` + found unit type `()` +help: try adding an expression at the end of the block + | +LL ~ } +LL + None + | +LL ~ } +LL + Some(()) + | + +error[E0308]: `?` operator has incompatible types + --> $DIR/compatible-variants.rs:35:5 + | +LL | c()? + | ^^^^ expected enum `Option`, found `()` + | + = note: `?` operator cannot convert from `()` to `Option<()>` + = note: expected enum `Option<()>` + found unit type `()` +help: try removing this `?` + | +LL - c()? +LL + c() + | +help: try adding an expression at the end of the block + | +LL ~ c()?; +LL + None + | +LL ~ c()?; +LL + Some(()) + | + +error[E0308]: mismatched types + --> $DIR/compatible-variants.rs:42:25 | LL | let _: Option<()> = while false {}; | ---------- ^^^^^^^^^^^^^^ expected enum `Option`, found `()` @@ -52,7 +97,7 @@ LL | let _: Option<()> = Some(while false {}); | +++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:31:9 + --> $DIR/compatible-variants.rs:46:9 | LL | while false {} | ^^^^^^^^^^^^^^ expected enum `Option`, found `()` @@ -69,7 +114,7 @@ LL + Some(()) | error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:35:31 + --> $DIR/compatible-variants.rs:50:31 | LL | let _: Result = 1; | ---------------- ^ expected enum `Result`, found integer @@ -86,7 +131,7 @@ LL | let _: Result = Err(1); | ++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:38:26 + --> $DIR/compatible-variants.rs:53:26 | LL | let _: Option = 1; | ----------- ^ expected enum `Option`, found integer @@ -101,7 +146,7 @@ LL | let _: Option = Some(1); | +++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:41:28 + --> $DIR/compatible-variants.rs:56:28 | LL | let _: Hey = 1; | ------------- ^ expected enum `Hey`, found integer @@ -118,7 +163,7 @@ LL | let _: Hey = Hey::B(1); | +++++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:44:29 + --> $DIR/compatible-variants.rs:59:29 | LL | let _: Hey = false; | -------------- ^^^^^ expected enum `Hey`, found `bool` @@ -133,7 +178,7 @@ LL | let _: Hey = Hey::B(false); | +++++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:48:19 + --> $DIR/compatible-variants.rs:63:19 | LL | let _ = Foo { bar }; | ^^^ expected enum `Option`, found `i32` @@ -145,6 +190,6 @@ help: try wrapping the expression in `Some` LL | let _ = Foo { bar: Some(bar) }; | ++++++++++ + -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0308`. From b32cabf458a67bdf6b511d385a315d23547dd157 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 2 Mar 2022 19:30:25 +0100 Subject: [PATCH 16/16] Update test output. --- src/test/ui/async-await/proper-span-for-type-error.fixed | 3 ++- src/test/ui/async-await/proper-span-for-type-error.stderr | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/ui/async-await/proper-span-for-type-error.fixed b/src/test/ui/async-await/proper-span-for-type-error.fixed index 1f1e1184dcc02..7d43b575d2f66 100644 --- a/src/test/ui/async-await/proper-span-for-type-error.fixed +++ b/src/test/ui/async-await/proper-span-for-type-error.fixed @@ -5,7 +5,8 @@ async fn a() {} async fn foo() -> Result<(), i32> { - Ok(a().await) //~ ERROR mismatched types + a().await; + Ok(()) //~ ERROR mismatched types } fn main() {} diff --git a/src/test/ui/async-await/proper-span-for-type-error.stderr b/src/test/ui/async-await/proper-span-for-type-error.stderr index 611dc0407bf96..25f05156ce2bc 100644 --- a/src/test/ui/async-await/proper-span-for-type-error.stderr +++ b/src/test/ui/async-await/proper-span-for-type-error.stderr @@ -6,10 +6,11 @@ LL | a().await | = note: expected enum `Result<(), i32>` found unit type `()` -help: try wrapping the expression in `Ok` +help: try adding an expression at the end of the block + | +LL ~ a().await; +LL ~ Ok(()) | -LL | Ok(a().await) - | +++ + error: aborting due to previous error