From b43aa960d0a7d09f137cc6b7f26605f6183cd72f Mon Sep 17 00:00:00 2001 From: johanngan Date: Sun, 10 Jan 2021 01:18:23 -0600 Subject: [PATCH 01/19] Print failure message on all tests that should panic, but don't This already happens with should_panic tests without an expected message. This commit fixes should_panic tests with an expected message to have the same behavior. --- library/test/src/test_result.rs | 2 +- library/test/src/tests.rs | 39 ++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 465f3f8f99427..598fb670bb4bf 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -63,7 +63,7 @@ pub fn calc_result<'a>( )) } } - (&ShouldPanic::Yes, Ok(())) => { + (&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => { TestResult::TrFailedMsg("test did not panic as expected".to_string()) } _ if desc.allow_fail => TestResult::TrAllowedFail, diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index 74313cc4438ed..a629829b88514 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -228,21 +228,30 @@ fn test_should_panic_non_string_message_type() { #[test] #[cfg(not(target_os = "emscripten"))] fn test_should_panic_but_succeeds() { - fn f() {} - let desc = TestDescAndFn { - desc: TestDesc { - name: StaticTestName("whatever"), - ignore: false, - should_panic: ShouldPanic::Yes, - allow_fail: false, - test_type: TestType::Unknown, - }, - testfn: DynTestFn(Box::new(f)), - }; - let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let result = rx.recv().unwrap().result; - assert_eq!(result, TrFailedMsg("test did not panic as expected".to_string())); + let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")]; + + for &should_panic in should_panic_variants.iter() { + fn f() {} + let desc = TestDescAndFn { + desc: TestDesc { + name: StaticTestName("whatever"), + ignore: false, + should_panic, + allow_fail: false, + test_type: TestType::Unknown, + }, + testfn: DynTestFn(Box::new(f)), + }; + let (tx, rx) = channel(); + run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + let result = rx.recv().unwrap().result; + assert_eq!( + result, + TrFailedMsg("test did not panic as expected".to_string()), + "should_panic == {:?}", + should_panic + ); + } } fn report_time_test_template(report_time: bool) -> Option { From 679f6f347355726f9335fdcbf0d3b81b2e490c38 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 10 Jan 2021 11:36:45 +0100 Subject: [PATCH 02/19] Add `unwrap_unchecked()` methods for `Option` and `Result` In particular: - `unwrap_unchecked()` for `Option`. - `unwrap_unchecked()` and `unwrap_err_unchecked()` for `Result`. These complement other `*_unchecked()` methods in `core` etc. Currently there are a couple of places it may be used inside rustc (`LinkedList`, `BTree`). It is also easy to find other repositories with similar functionality. Fixes #48278. Signed-off-by: Miguel Ojeda --- library/core/src/option.rs | 31 +++++++++++++++++ library/core/src/result.rs | 64 +++++++++++++++++++++++++++++++++++- library/core/tests/lib.rs | 1 + library/core/tests/option.rs | 7 ++++ library/core/tests/result.rs | 12 +++++++ 5 files changed, 114 insertions(+), 1 deletion(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 0051c9eede070..18b494b3175b6 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -428,6 +428,37 @@ impl Option { } } + /// Returns the contained [`Some`] value, consuming the `self` value, + /// without checking that the value is not [`None`]. + /// + /// # Safety + /// + /// Undefined behavior if the value is [`None`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_result_unwrap_unchecked)] + /// let x = Some("air"); + /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air"); + /// ``` + /// + /// ```no_run + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Option<&str> = None; + /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air"); // Undefined behavior! + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + pub unsafe fn unwrap_unchecked(self) -> T { + debug_assert!(self.is_some()); + match self { + Some(val) => val, + None => unsafe { hint::unreachable_unchecked() }, + } + } + ///////////////////////////////////////////////////////////////////////// // Transforming contained values ///////////////////////////////////////////////////////////////////////// diff --git a/library/core/src/result.rs b/library/core/src/result.rs index d6d1762572928..a0f5c7746cc75 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -229,7 +229,7 @@ use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; use crate::ops::{self, Deref, DerefMut}; -use crate::{convert, fmt}; +use crate::{convert, fmt, hint}; /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). /// @@ -821,6 +821,68 @@ impl Result { Err(e) => op(e), } } + + /// Returns the contained [`Ok`] value, consuming the `self` value, + /// without checking that the value is not an [`Err`]. + /// + /// # Safety + /// + /// Undefined behavior if the value is an [`Err`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Ok(2); + /// assert_eq!(unsafe { x.unwrap_unchecked() }, 2); + /// ``` + /// + /// ```no_run + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Err("emergency failure"); + /// unsafe { x.unwrap_unchecked(); } // Undefined behavior! + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + pub unsafe fn unwrap_unchecked(self) -> T { + debug_assert!(self.is_ok()); + match self { + Ok(t) => t, + Err(_) => unsafe { hint::unreachable_unchecked() }, + } + } + + /// Returns the contained [`Err`] value, consuming the `self` value, + /// without checking that the value is not an [`Ok`]. + /// + /// # Safety + /// + /// Undefined behavior if the value is an [`Ok`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Ok(2); + /// unsafe { x.unwrap_err_unchecked() }; // Undefined behavior! + /// ``` + /// + /// ``` + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Err("emergency failure"); + /// assert_eq!(unsafe { x.unwrap_err_unchecked() }, "emergency failure"); + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + pub unsafe fn unwrap_err_unchecked(self) -> E { + debug_assert!(self.is_err()); + match self { + Ok(_) => unsafe { hint::unreachable_unchecked() }, + Err(e) => e, + } + } } impl Result<&T, E> { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index e01aaa4cbf179..285e6cdfd39f9 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -62,6 +62,7 @@ #![feature(const_raw_ptr_deref)] #![feature(never_type)] #![feature(unwrap_infallible)] +#![feature(option_result_unwrap_unchecked)] #![feature(option_unwrap_none)] #![feature(peekable_next_if)] #![feature(peekable_peek_mut)] diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index 5388b4756245a..9470451278cc4 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -160,6 +160,13 @@ fn test_unwrap_or_else() { assert_eq!(x.unwrap_or_else(|| 2), 2); } +#[test] +fn test_unwrap_unchecked() { + assert_eq!(unsafe { Some(1).unwrap_unchecked() }, 1); + let s = unsafe { Some("hello".to_string()).unwrap_unchecked() }; + assert_eq!(s, "hello"); +} + #[test] fn test_iter() { let val = 5; diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs index 81660870e95e4..7aa44c6e593b3 100644 --- a/library/core/tests/result.rs +++ b/library/core/tests/result.rs @@ -119,6 +119,18 @@ pub fn test_unwrap_or_else_panic() { let _: isize = bad_err.unwrap_or_else(handler); } +#[test] +fn test_unwrap_unchecked() { + let ok: Result = Ok(100); + assert_eq!(unsafe { ok.unwrap_unchecked() }, 100); +} + +#[test] +fn test_unwrap_err_unchecked() { + let ok_err: Result = Err("Err"); + assert_eq!(unsafe { ok_err.unwrap_err_unchecked() }, "Err"); +} + #[test] pub fn test_expect_ok() { let ok: Result = Ok(100); From 76299b3f42a402aa896b76fccd725f52080f374d Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 10 Jan 2021 15:59:17 +0100 Subject: [PATCH 03/19] Add `SAFETY` annotations Signed-off-by: Miguel Ojeda --- library/core/src/option.rs | 1 + library/core/src/result.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 18b494b3175b6..5d34f5ca155bf 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -455,6 +455,7 @@ impl Option { debug_assert!(self.is_some()); match self { Some(val) => val, + // SAFETY: the safety contract must be upheld by the caller. None => unsafe { hint::unreachable_unchecked() }, } } diff --git a/library/core/src/result.rs b/library/core/src/result.rs index a0f5c7746cc75..a357750b92f9d 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -849,6 +849,7 @@ impl Result { debug_assert!(self.is_ok()); match self { Ok(t) => t, + // SAFETY: the safety contract must be upheld by the caller. Err(_) => unsafe { hint::unreachable_unchecked() }, } } @@ -879,6 +880,7 @@ impl Result { pub unsafe fn unwrap_err_unchecked(self) -> E { debug_assert!(self.is_err()); match self { + // SAFETY: the safety contract must be upheld by the caller. Ok(_) => unsafe { hint::unreachable_unchecked() }, Err(e) => e, } From 63a1eeea234105fd9c1ed30ac2b6e0d9bf41a1e9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 18 Jan 2021 11:55:23 -0600 Subject: [PATCH 04/19] Reset LateContext enclosing body in nested items Prevents LateContext::maybe_typeck_results() from returning data in a nested item without a body. Consequently, LateContext::qpath_res is less likely to ICE when called in a nested item. Would have prevented rust-lang/rust-clippy#4545, presumably. --- compiler/rustc_lint/src/late.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 015e109871182..3821a393efb8b 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -140,6 +140,8 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { let generics = self.context.generics.take(); self.context.generics = it.kind.generics(); + let old_cached_typeck_results = self.context.cached_typeck_results.take(); + let old_enclosing_body = self.context.enclosing_body.take(); self.with_lint_attrs(it.hir_id, &it.attrs, |cx| { cx.with_param_env(it.hir_id, |cx| { lint_callback!(cx, check_item, it); @@ -147,6 +149,8 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas lint_callback!(cx, check_item_post, it); }); }); + self.context.enclosing_body = old_enclosing_body; + self.context.cached_typeck_results.set(old_cached_typeck_results); self.context.generics = generics; } From 21fb586c0c72a546bae445df1588cef464502ada Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 18 Jan 2021 13:29:37 -0600 Subject: [PATCH 05/19] Query for TypeckResults in LateContext::qpath_res Actually fulfills the documented guarantees. --- compiler/rustc_lint/src/context.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 3971a3099823f..3f92d66d88bce 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -746,6 +746,14 @@ impl<'tcx> LateContext<'tcx> { hir::QPath::Resolved(_, ref path) => path.res, hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self .maybe_typeck_results() + .filter(|typeck_results| typeck_results.hir_owner == id.owner) + .or_else(|| { + if self.tcx.has_typeck_results(id.owner.to_def_id()) { + Some(self.tcx.typeck(id.owner)) + } else { + None + } + }) .and_then(|typeck_results| typeck_results.type_dependent_def(id)) .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), } From def0e9b8a495fa2a44e1d2cd948f7d4b6d81344f Mon Sep 17 00:00:00 2001 From: Camelid Date: Sun, 10 Jan 2021 19:55:54 -0800 Subject: [PATCH 06/19] Fix ICE with `ReadPointerAsBytes` validation error --- compiler/rustc_mir/src/interpret/validity.rs | 6 +++- src/test/ui/issues/issue-79690.rs | 29 ++++++++++++++++++++ src/test/ui/issues/issue-79690.stderr | 11 ++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/issues/issue-79690.rs create mode 100644 src/test/ui/issues/issue-79690.stderr diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 423d1270ac865..0b7492631c41d 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -515,7 +515,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' Ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { - let value = self.ecx.read_scalar(value)?; + let value = try_validation!( + self.ecx.read_scalar(value), + self.path, + err_unsup!(ReadPointerAsBytes) => { "read of part of a pointer" }, + ); // NOTE: Keep this in sync with the array optimization for int/float // types below! if self.ctfe_mode.is_some() { diff --git a/src/test/ui/issues/issue-79690.rs b/src/test/ui/issues/issue-79690.rs new file mode 100644 index 0000000000000..6a38b3c8c2c71 --- /dev/null +++ b/src/test/ui/issues/issue-79690.rs @@ -0,0 +1,29 @@ +union Transmute { + t: T, + u: U, +} +trait Bar { + fn bar(&self) -> u32; +} +struct Foo { + foo: u32, + bar: bool, +} +impl Bar for Foo { + fn bar(&self) -> u32 { + self.foo + } +} +#[derive(Copy, Clone)] +struct Fat<'a>(&'a Foo, &'static VTable); +struct VTable { + size: Foo, +} +const FOO: &dyn Bar = &Foo { + foo: 128, + bar: false, +}; +const G: Fat = unsafe { Transmute { t: FOO }.u }; +//~^ ERROR it is undefined behavior to use this value + +fn main() {} diff --git a/src/test/ui/issues/issue-79690.stderr b/src/test/ui/issues/issue-79690.stderr new file mode 100644 index 0000000000000..c7f9c6a55e712 --- /dev/null +++ b/src/test/ui/issues/issue-79690.stderr @@ -0,0 +1,11 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/issue-79690.rs:26:1 + | +LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered read of part of a pointer at .1..size.foo + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. From a7b7a435ea14cd0d2b83ea8a229e7d9cc4c651f1 Mon Sep 17 00:00:00 2001 From: Camelid Date: Sun, 10 Jan 2021 20:40:47 -0800 Subject: [PATCH 07/19] Move test to `src/test/ui/consts/` Apparently `tidy` has a hard limit of 2830 tests in the `src/test/ui/issues/` directory, and this test hit that limit. `src/test/ui/consts/` is probably a better location anyway. --- src/test/ui/{issues => consts}/issue-79690.rs | 0 src/test/ui/{issues => consts}/issue-79690.stderr | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/test/ui/{issues => consts}/issue-79690.rs (100%) rename src/test/ui/{issues => consts}/issue-79690.stderr (100%) diff --git a/src/test/ui/issues/issue-79690.rs b/src/test/ui/consts/issue-79690.rs similarity index 100% rename from src/test/ui/issues/issue-79690.rs rename to src/test/ui/consts/issue-79690.rs diff --git a/src/test/ui/issues/issue-79690.stderr b/src/test/ui/consts/issue-79690.stderr similarity index 100% rename from src/test/ui/issues/issue-79690.stderr rename to src/test/ui/consts/issue-79690.stderr From eaba3daa60d789997c0be3da11619df2469e9a7e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 18 Jan 2021 13:36:32 -0600 Subject: [PATCH 08/19] Remove qpath_res util function --- src/tools/clippy/clippy_lints/src/default.rs | 4 ++-- .../clippy_lints/src/drop_forget_ref.rs | 4 ++-- src/tools/clippy/clippy_lints/src/exit.rs | 4 ++-- .../clippy/clippy_lints/src/functions.rs | 8 +++---- .../clippy/clippy_lints/src/let_if_seq.rs | 4 ++-- src/tools/clippy/clippy_lints/src/loops.rs | 22 +++++++++---------- .../clippy/clippy_lints/src/manual_strip.rs | 8 +++---- .../clippy/clippy_lints/src/mem_forget.rs | 4 ++-- .../clippy/clippy_lints/src/no_effect.rs | 6 ++--- .../clippy/clippy_lints/src/non_copy_const.rs | 4 ++-- .../clippy_lints/src/to_string_in_display.rs | 4 ++-- src/tools/clippy/clippy_lints/src/types.rs | 12 +++++----- .../clippy_lints/src/utils/internal_lints.rs | 6 ++--- .../clippy/clippy_lints/src/utils/mod.rs | 13 ----------- 14 files changed, 45 insertions(+), 58 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index f7224811e6e79..6fa1378b8c73d 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -1,5 +1,5 @@ use crate::utils::{ - any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet_with_macro_callsite, + any_parent_is_automatically_derived, contains_name, match_def_path, paths, snippet_with_macro_callsite, }; use crate::utils::{span_lint_and_note, span_lint_and_sugg}; use if_chain::if_chain; @@ -231,7 +231,7 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool if_chain! { if let ExprKind::Call(ref fn_expr, _) = &expr.kind; if let ExprKind::Path(qpath) = &fn_expr.kind; - if let Res::Def(_, def_id) = qpath_res(cx, qpath, fn_expr.hir_id); + if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); then { // right hand side of assignment is `Default::default` match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD) diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index cf528d189b4b1..a84f9c4628716 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_copy, match_def_path, paths, qpath_res, span_lint_and_note}; +use crate::utils::{is_copy, match_def_path, paths, span_lint_and_note}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { if let ExprKind::Call(ref path, ref args) = expr.kind; if let ExprKind::Path(ref qpath) = path.kind; if args.len() == 1; - if let Some(def_id) = qpath_res(cx, qpath, path.hir_id).opt_def_id(); + if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); then { let lint; let msg; diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index 7337d98c8be37..915859270009b 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_entrypoint_fn, match_def_path, paths, qpath_res, span_lint}; +use crate::utils::{is_entrypoint_fn, match_def_path, paths, span_lint}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; @@ -29,7 +29,7 @@ impl<'tcx> LateLintPass<'tcx> for Exit { if_chain! { if let ExprKind::Call(ref path_expr, ref _args) = e.kind; if let ExprKind::Path(ref path) = path_expr.kind; - if let Some(def_id) = qpath_res(cx, path, path_expr.hir_id).opt_def_id(); + if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::EXIT); then { let parent = cx.tcx.hir().get_parent_item(e.hir_id); diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs index fd93548b55c6d..8795425461033 100644 --- a/src/tools/clippy/clippy_lints/src/functions.rs +++ b/src/tools/clippy/clippy_lints/src/functions.rs @@ -1,7 +1,7 @@ use crate::utils::{ attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats, - last_path_segment, match_def_path, must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, - span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, + last_path_segment, match_def_path, must_use_attr, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, + span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, }; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -659,7 +659,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { fn check_arg(&self, ptr: &hir::Expr<'_>) { if let hir::ExprKind::Path(ref qpath) = ptr.kind { - if let Res::Local(id) = qpath_res(self.cx, qpath, ptr.hir_id) { + if let Res::Local(id) = self.cx.qpath_res(qpath, ptr.hir_id) { if self.ptrs.contains(&id) { span_lint( self.cx, @@ -722,7 +722,7 @@ fn is_mutated_static(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool { use hir::ExprKind::{Field, Index, Path}; match e.kind { - Path(ref qpath) => !matches!(qpath_res(cx, qpath, e.hir_id), Res::Local(_)), + Path(ref qpath) => !matches!(cx.qpath_res(qpath, e.hir_id), Res::Local(_)), Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner), _ => false, } diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs index db717cd1240a4..5886c2360e362 100644 --- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs +++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs @@ -1,4 +1,4 @@ -use crate::utils::{qpath_res, snippet, span_lint_and_then, visitors::LocalUsedVisitor}; +use crate::utils::{snippet, span_lint_and_then, visitors::LocalUsedVisitor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -145,7 +145,7 @@ fn check_assign<'tcx>( if let hir::StmtKind::Semi(ref expr) = expr.kind; if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind; if let hir::ExprKind::Path(ref qpath) = var.kind; - if let Res::Local(local_id) = qpath_res(cx, qpath, var.hir_id); + if let Res::Local(local_id) = cx.qpath_res(qpath, var.hir_id); if decl == local_id; then { let mut v = LocalUsedVisitor::new(decl); diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs index 1c5ab2874b048..1ae2c6ca95788 100644 --- a/src/tools/clippy/clippy_lints/src/loops.rs +++ b/src/tools/clippy/clippy_lints/src/loops.rs @@ -6,9 +6,9 @@ use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, - last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, - snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, - span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, + last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, single_segment_path, snippet, + snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, + span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -848,7 +848,7 @@ fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { if let ExprKind::Path(qpath) = &expr.kind; if let QPath::Resolved(None, path) = qpath; if path.segments.len() == 1; - if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id); + if let Res::Local(local_id) = cx.qpath_res(qpath, expr.hir_id); then { // our variable! local_id == var @@ -1420,7 +1420,7 @@ fn detect_same_item_push<'tcx>( // Make sure that the push does not involve possibly mutating values match pushed_item.kind { ExprKind::Path(ref qpath) => { - match qpath_res(cx, qpath, pushed_item.hir_id) { + match cx.qpath_res(qpath, pushed_item.hir_id) { // immutable bindings that are initialized with literal or constant Res::Local(hir_id) => { if_chain! { @@ -1437,7 +1437,7 @@ fn detect_same_item_push<'tcx>( ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), // immutable bindings that are initialized with constant ExprKind::Path(ref path) => { - if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { + if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { emit_lint(cx, vec, pushed_item); } } @@ -2028,7 +2028,7 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option if let ExprKind::Path(ref qpath) = bound.kind; if let QPath::Resolved(None, _) = *qpath; then { - let res = qpath_res(cx, qpath, bound.hir_id); + let res = cx.qpath_res(qpath, bound.hir_id); if let Res::Local(hir_id) = res { let node_str = cx.tcx.hir().get(hir_id); if_chain! { @@ -2120,7 +2120,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { if self.prefer_mutable { self.indexed_mut.insert(seqvar.segments[0].ident.name); } - let res = qpath_res(self.cx, seqpath, seqexpr.hir_id); + let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); match res { Res::Local(hir_id) => { let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); @@ -2184,7 +2184,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if let QPath::Resolved(None, ref path) = *qpath; if path.segments.len() == 1; then { - if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id) { + if let Res::Local(local_id) = self.cx.qpath_res(qpath, expr.hir_id) { if local_id == self.var { self.nonindex = true; } else { @@ -2589,7 +2589,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { fn var_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::Path(ref qpath) = expr.kind { - let path_res = qpath_res(cx, qpath, expr.hir_id); + let path_res = cx.qpath_res(qpath, expr.hir_id); if let Res::Local(hir_id) = path_res { return Some(hir_id); } @@ -2819,7 +2819,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { if_chain! { if let ExprKind::Path(ref qpath) = ex.kind; if let QPath::Resolved(None, _) = *qpath; - let res = qpath_res(self.cx, qpath, ex.hir_id); + let res = self.cx.qpath_res(qpath, ex.hir_id); then { match res { Res::Local(hir_id) => { diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index a0cfe145a301c..42a92104a4919 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -1,7 +1,7 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, + eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, snippet, span_lint_and_then, }; use if_chain::if_chain; @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { } else { return; }; - let target_res = qpath_res(cx, &target_path, target_arg.hir_id); + let target_res = cx.qpath_res(&target_path, target_arg.hir_id); if target_res == Res::Err { return; }; @@ -221,7 +221,7 @@ fn find_stripping<'tcx>( if let ExprKind::Index(indexed, index) = &unref.kind; if let Some(higher::Range { start, end, .. }) = higher::range(index); if let ExprKind::Path(path) = &indexed.kind; - if qpath_res(self.cx, path, ex.hir_id) == self.target; + if self.cx.qpath_res(path, ex.hir_id) == self.target; then { match (self.strip_kind, start, end) { (StripKind::Prefix, Some(start), None) => { @@ -235,7 +235,7 @@ fn find_stripping<'tcx>( if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind; if let Some(left_arg) = len_arg(self.cx, left); if let ExprKind::Path(left_path) = &left_arg.kind; - if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target; + if self.cx.qpath_res(left_path, left_arg.hir_id) == self.target; if eq_pattern_length(self.cx, self.pattern, right); then { self.results.push(ex.span); diff --git a/src/tools/clippy/clippy_lints/src/mem_forget.rs b/src/tools/clippy/clippy_lints/src/mem_forget.rs index 8c6fd10f98a1e..d34f9761e26f9 100644 --- a/src/tools/clippy/clippy_lints/src/mem_forget.rs +++ b/src/tools/clippy/clippy_lints/src/mem_forget.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_def_path, paths, qpath_res, span_lint}; +use crate::utils::{match_def_path, paths, span_lint}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -29,7 +29,7 @@ impl<'tcx> LateLintPass<'tcx> for MemForget { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Call(ref path_expr, ref args) = e.kind { if let ExprKind::Path(ref qpath) = path_expr.kind { - if let Some(def_id) = qpath_res(cx, qpath, path_expr.hir_id).opt_def_id() { + if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() { if match_def_path(cx, def_id, &paths::MEM_FORGET) { let forgot_ty = cx.typeck_results().expr_ty(&args[0]); diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index b1b5b3439a0e3..69302d695ce0a 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -1,4 +1,4 @@ -use crate::utils::{has_drop, qpath_res, snippet_opt, span_lint, span_lint_and_sugg}; +use crate::utils::{has_drop, snippet_opt, span_lint, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource}; @@ -67,7 +67,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }, ExprKind::Call(ref callee, ref args) => { if let ExprKind::Path(ref qpath) = callee.kind { - let res = qpath_res(cx, qpath, callee.hir_id); + let res = cx.qpath_res(qpath, callee.hir_id); match res { Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) => { !has_drop(cx, cx.typeck_results().expr_ty(expr)) @@ -146,7 +146,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option { if let ExprKind::Path(ref qpath) = callee.kind { - let res = qpath_res(cx, qpath, callee.hir_id); + let res = cx.qpath_res(qpath, callee.hir_id); match res { Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) if !has_drop(cx, cx.typeck_results().expr_ty(expr)) => diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 3a9aa6ced03ba..f57d753631755 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -18,7 +18,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{in_constant, qpath_res, span_lint_and_then}; +use crate::utils::{in_constant, span_lint_and_then}; use if_chain::if_chain; // FIXME: this is a correctness problem but there's no suitable @@ -339,7 +339,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } // Make sure it is a const item. - let item_def_id = match qpath_res(cx, qpath, expr.hir_id) { + let item_def_id = match cx.qpath_res(qpath, expr.hir_id) { Res::Def(DefKind::Const | DefKind::AssocConst, did) => did, _ => return, }; diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs index c53727ba16004..fa508df865e48 100644 --- a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs +++ b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint}; +use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; use if_chain::if_chain; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind}; @@ -94,7 +94,7 @@ impl LateLintPass<'_> for ToStringInDisplay { if match_trait_method(cx, expr, &paths::TO_STRING); if self.in_display_impl; if let ExprKind::Path(ref qpath) = args[0].kind; - if let Res::Local(hir_id) = qpath_res(cx, qpath, args[0].hir_id); + if let Res::Local(hir_id) = cx.qpath_res(qpath, args[0].hir_id); if let Some(self_hir_id) = self.self_hir_id; if hir_id == self_hir_id; then { diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs index 3b5a83d2a0bec..624ea16f585d2 100644 --- a/src/tools/clippy/clippy_lints/src/types.rs +++ b/src/tools/clippy/clippy_lints/src/types.rs @@ -34,7 +34,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_hir_ty_cfg_dependant, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, - multispan_sugg, numeric_literal::NumericLiteral, qpath_res, reindent_multiline, sext, snippet, snippet_opt, + multispan_sugg, numeric_literal::NumericLiteral, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; @@ -298,7 +298,7 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str]) _ => None, }); if let TyKind::Path(ref qpath) = ty.kind; - if let Some(did) = qpath_res(cx, qpath, ty.hir_id).opt_def_id(); + if let Some(did) = cx.qpath_res(qpath, ty.hir_id).opt_def_id(); if match_def_path(cx, did, path); then { return Some(ty.span); @@ -365,7 +365,7 @@ impl Types { match hir_ty.kind { TyKind::Path(ref qpath) if !is_local => { let hir_id = hir_ty.hir_id; - let res = qpath_res(cx, qpath, hir_id); + let res = cx.qpath_res(qpath, hir_id); if let Some(def_id) = res.opt_def_id() { if Some(def_id) == cx.tcx.lang_items().owned_box() { if let Some(span) = match_borrows_parameter(cx, qpath) { @@ -535,7 +535,7 @@ impl Types { }); // ty is now _ at this point if let TyKind::Path(ref ty_qpath) = ty.kind; - let res = qpath_res(cx, ty_qpath, ty.hir_id); + let res = cx.qpath_res(ty_qpath, ty.hir_id); if let Some(def_id) = res.opt_def_id(); if Some(def_id) == cx.tcx.lang_items().owned_box(); // At this point, we know ty is Box, now get T @@ -652,7 +652,7 @@ impl Types { match mut_ty.ty.kind { TyKind::Path(ref qpath) => { let hir_id = mut_ty.ty.hir_id; - let def = qpath_res(cx, qpath, hir_id); + let def = cx.qpath_res(qpath, hir_id); if_chain! { if let Some(def_id) = def.opt_def_id(); if Some(def_id) == cx.tcx.lang_items().owned_box(); @@ -739,7 +739,7 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool { fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option> { if_chain! { - if let Some(did) = qpath_res(cx, qpath, id).opt_def_id(); + if let Some(did) = cx.qpath_res(qpath, id).opt_def_id(); if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; if synthetic == Some(SyntheticTyParamKind::ImplTrait); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 7aa17520ba79f..822863ca3e279 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -1,7 +1,7 @@ use crate::consts::{constant_simple, Constant}; use crate::utils::{ - is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, - snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, + is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, run_lints, snippet, + span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; @@ -787,7 +787,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option return path_to_matched_type(cx, expr), - ExprKind::Path(qpath) => match qpath_res(cx, qpath, expr.hir_id) { + ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { let parent_id = cx.tcx.hir().get_parent_node(hir_id); if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) { diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 9b262517a9834..023fb0a7112c1 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -370,19 +370,6 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { } } -pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { - match qpath { - hir::QPath::Resolved(_, path) => path.res, - hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => { - if cx.tcx.has_typeck_results(id.owner.to_def_id()) { - cx.tcx.typeck(id.owner).qpath_res(qpath, id) - } else { - Res::Err - } - }, - } -} - /// Convenience function to get the `DefId` of a trait by path. /// It could be a trait or trait alias. pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option { From e25959b417fe2a08d808f9ddc737c2c9742d6d6a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 22 Jan 2021 18:07:00 +0100 Subject: [PATCH 09/19] Make more traits of the From/Into family diagnostic items Following traits are now diagnostic items: - `From` (unchanged) - `Into` - `TryFrom` - `TryInto` This also adds symbols for those items: - `into_trait` - `try_from_trait` - `try_into_trait` --- compiler/rustc_span/src/symbol.rs | 3 +++ library/core/src/convert/mod.rs | 3 +++ src/tools/clippy/clippy_lints/src/fallible_impl_from.rs | 7 ++----- src/tools/clippy/clippy_lints/src/utils/paths.rs | 1 - 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7b90e5b611cd1..2e7c9701c0c67 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -622,6 +622,7 @@ symbols! { intel, into_iter, into_result, + into_trait, intra_doc_pointers, intrinsics, irrefutable_let_patterns, @@ -1159,6 +1160,8 @@ symbols! { truncf32, truncf64, try_blocks, + try_from_trait, + try_into_trait, try_trait, tt, tuple, diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 139863bbe7f81..a6d3b5ef813f1 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -267,6 +267,7 @@ pub trait AsMut { /// /// [`String`]: ../../std/string/struct.String.html /// [`Vec`]: ../../std/vec/struct.Vec.html +#[rustc_diagnostic_item = "into_trait"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Into: Sized { /// Performs the conversion. @@ -382,6 +383,7 @@ pub trait From: Sized { /// /// This suffers the same restrictions and reasoning as implementing /// [`Into`], see there for details. +#[rustc_diagnostic_item = "try_into_trait"] #[stable(feature = "try_from", since = "1.34.0")] pub trait TryInto: Sized { /// The type returned in the event of a conversion error. @@ -462,6 +464,7 @@ pub trait TryInto: Sized { /// /// [`try_from`]: TryFrom::try_from /// [`!`]: ../../std/primitive.never.html +#[rustc_diagnostic_item = "try_from_trait"] #[stable(feature = "try_from", since = "1.34.0")] pub trait TryFrom: Sized { /// The type returned in the event of a conversion error. diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index 9f389c8d2f9e7..527905e375d28 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -1,7 +1,4 @@ -use crate::utils::paths::FROM_TRAIT; -use crate::utils::{ - is_expn_of, is_type_diagnostic_item, match_def_path, match_panic_def_id, method_chain_args, span_lint_and_then, -}; +use crate::utils::{is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args, span_lint_and_then}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -59,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { if_chain! { if let hir::ItemKind::Impl(impl_) = &item.kind; if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id); - if match_def_path(cx, impl_trait_ref.def_id, &FROM_TRAIT); + if cx.tcx.is_diagnostic_item(sym::from_trait, impl_trait_ref.def_id); then { lint_impl_body(cx, item.span, impl_.items); } diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs index c0b203b5388dc..432cc5b59f684 100644 --- a/src/tools/clippy/clippy_lints/src/utils/paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs @@ -48,7 +48,6 @@ pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; -pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; From 48f9dbfd59356f865f81ce674eefdbab2d7c3cbb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 24 Jan 2021 12:50:30 +0100 Subject: [PATCH 10/19] clean up some const error reporting around promoteds --- .../rustc_codegen_cranelift/src/constant.rs | 8 +- .../rustc_codegen_ssa/src/mir/constant.rs | 7 +- .../rustc_mir/src/const_eval/eval_queries.rs | 102 +++++------------- .../defaults-not-assumed-fail.rs | 2 +- .../defaults-not-assumed-fail.stderr | 10 +- .../conditional_array_execution.stderr | 2 +- .../const-eval/const-eval-query-stack.rs | 2 +- .../const-eval/const-eval-query-stack.stderr | 8 +- .../consts/const-eval/const_fn_ptr_fail2.rs | 4 +- .../const-eval/const_fn_ptr_fail2.stderr | 20 ++-- src/test/ui/consts/const-eval/issue-43197.rs | 4 +- .../ui/consts/const-eval/issue-43197.stderr | 4 +- src/test/ui/consts/const-eval/issue-44578.rs | 2 +- .../ui/consts/const-eval/issue-44578.stderr | 2 +- .../ui/consts/const-eval/issue-50814-2.stderr | 8 +- .../ui/consts/const-eval/issue-50814.stderr | 8 +- .../consts/const_unsafe_unreachable_ub.stderr | 10 +- src/test/ui/consts/issue-55878.stderr | 7 +- 18 files changed, 66 insertions(+), 144 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index beff84fb2e217..5702832bcb67d 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -134,11 +134,9 @@ pub(crate) fn codegen_constant<'tcx>( { Ok(const_val) => const_val, Err(_) => { - if promoted.is_none() { - fx.tcx - .sess - .span_err(constant.span, "erroneous constant encountered"); - } + fx.tcx + .sess + .span_err(constant.span, "erroneous constant encountered"); return crate::trap::trap_unreachable_ret_value( fx, fx.layout_of(const_.ty), diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 3a85c268e0ea9..b79a221a0e74a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -30,12 +30,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { .tcx() .const_eval_resolve(ty::ParamEnv::reveal_all(), def, substs, promoted, None) .map_err(|err| { - if promoted.is_none() { - self.cx - .tcx() - .sess - .span_err(constant.span, "erroneous constant encountered"); - } + self.cx.tcx().sess.span_err(constant.span, "erroneous constant encountered"); err }), ty::ConstKind::Value(value) => Ok(value), diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index df163f6562842..252f5e7ef2ff2 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -298,6 +298,8 @@ pub fn eval_to_allocation_raw_provider<'tcx>( tcx.def_span(def.did), key.param_env, CompileTimeInterpreter::new(tcx.sess.const_eval_limit()), + // Statics (and promoteds inside statics) may access other statics, because unlike consts + // they do not have to behave "as if" they were evaluated at runtime. MemoryExtra { can_access_statics: is_static }, ); @@ -305,83 +307,35 @@ pub fn eval_to_allocation_raw_provider<'tcx>( match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { Err(error) => { let err = ConstEvalErr::new(&ecx, error, None); - // errors in statics are always emitted as fatal errors - if is_static { - // Ensure that if the above error was either `TooGeneric` or `Reported` - // an error must be reported. - let v = err.report_as_error( - ecx.tcx.at(ecx.cur_span()), - "could not evaluate static initializer", - ); - - // If this is `Reveal:All`, then we need to make sure an error is reported but if - // this is `Reveal::UserFacing`, then it's expected that we could get a - // `TooGeneric` error. When we fall back to `Reveal::All`, then it will either - // succeed or we'll report this error then. - if key.param_env.reveal() == Reveal::All { - tcx.sess.delay_span_bug( - err.span, - &format!("static eval failure did not emit an error: {:#?}", v), - ); - } - - Err(v) - } else if let Some(def) = def.as_local() { - // constant defined in this crate, we can figure out a lint level! - match tcx.def_kind(def.did.to_def_id()) { - // constants never produce a hard error at the definition site. Anything else is - // a backwards compatibility hazard (and will break old versions of winapi for - // sure) - // - // note that validation may still cause a hard error on this very same constant, - // because any code that existed before validation could not have failed - // validation thus preventing such a hard error from being a backwards - // compatibility hazard - DefKind::Const | DefKind::AssocConst => { - let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - Err(err.report_as_lint( - tcx.at(tcx.def_span(def.did)), - "any use of this value will cause an error", - hir_id, - Some(err.span), - )) - } - // promoting runtime code is only allowed to error if it references broken - // constants any other kind of error will be reported to the user as a - // deny-by-default lint - _ => { - if let Some(p) = cid.promoted { - let span = tcx.promoted_mir_opt_const_arg(def.to_global())[p].span; - if let err_inval!(ReferencedConstant) = err.error { - Err(err.report_as_error( - tcx.at(span), - "evaluation of constant expression failed", - )) - } else { - Err(err.report_as_lint( - tcx.at(span), - "reaching this expression at runtime will panic or abort", - tcx.hir().local_def_id_to_hir_id(def.did), - Some(err.span), - )) - } - // anything else (array lengths, enum initializers, constant patterns) are - // reported as hard errors - } else { - Err(err.report_as_error( - ecx.tcx.at(ecx.cur_span()), - "evaluation of constant value failed", - )) - } - } - } + // Some CTFE errors raise just a lint, not a hard error; see + // . + let emit_as_lint = if let Some(def) = def.as_local() { + // (Associated) consts only emit a lint, since they might be unused. + matches!(tcx.def_kind(def.did.to_def_id()), DefKind::Const | DefKind::AssocConst) } else { - // use of broken constant from other crate - Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant")) + // use of broken constant from other crate: always an error + false + }; + if emit_as_lint { + let hir_id = tcx.hir().local_def_id_to_hir_id(def.as_local().unwrap().did); + Err(err.report_as_lint( + tcx.at(tcx.def_span(def.did)), + "any use of this value will cause an error", + hir_id, + Some(err.span), + )) + } else { + let msg = if is_static { + "could not evaluate static initializer" + } else { + "evaluation of constant value failed" + }; + Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), msg)) } } Ok(mplace) => { - // Since evaluation had no errors, valiate the resulting constant: + // Since evaluation had no errors, validate the resulting constant. + // This is a separate `try` block to provide more targeted error reporting. let validation = try { let mut ref_tracking = RefTracking::new(mplace); let mut inner = false; @@ -399,7 +353,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( } }; if let Err(error) = validation { - // Validation failed, report an error + // Validation failed, report an error. This is always a hard error. let err = ConstEvalErr::new(&ecx, error, None); Err(err.struct_error( ecx.tcx, diff --git a/src/test/ui/associated-consts/defaults-not-assumed-fail.rs b/src/test/ui/associated-consts/defaults-not-assumed-fail.rs index d7a48cbd63ecc..b0a4c7722e3ce 100644 --- a/src/test/ui/associated-consts/defaults-not-assumed-fail.rs +++ b/src/test/ui/associated-consts/defaults-not-assumed-fail.rs @@ -31,7 +31,7 @@ impl Tr for u32 { fn main() { assert_eq!(<() as Tr>::A, 255); assert_eq!(<() as Tr>::B, 0); // causes the error above - //~^ ERROR evaluation of constant expression failed + //~^ ERROR evaluation of constant value failed //~| ERROR erroneous constant used assert_eq!(::A, 254); diff --git a/src/test/ui/associated-consts/defaults-not-assumed-fail.stderr b/src/test/ui/associated-consts/defaults-not-assumed-fail.stderr index 1497633c26af9..cbaaed0508b98 100644 --- a/src/test/ui/associated-consts/defaults-not-assumed-fail.stderr +++ b/src/test/ui/associated-consts/defaults-not-assumed-fail.stderr @@ -8,15 +8,11 @@ LL | const B: u8 = Self::A + 1; | = note: `#[deny(const_err)]` on by default -error[E0080]: evaluation of constant expression failed - --> $DIR/defaults-not-assumed-fail.rs:33:5 +error[E0080]: evaluation of constant value failed + --> $DIR/defaults-not-assumed-fail.rs:33:16 | LL | assert_eq!(<() as Tr>::B, 0); // causes the error above - | ^^^^^^^^^^^-------------^^^^^ - | | - | referenced constant has errors - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^^^^^^^^ referenced constant has errors error: erroneous constant used --> $DIR/defaults-not-assumed-fail.rs:33:5 diff --git a/src/test/ui/consts/const-eval/conditional_array_execution.stderr b/src/test/ui/consts/const-eval/conditional_array_execution.stderr index 65dfbd8097e76..c2adff116ef20 100644 --- a/src/test/ui/consts/const-eval/conditional_array_execution.stderr +++ b/src/test/ui/consts/const-eval/conditional_array_execution.stderr @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![warn(const_err)] | ^^^^^^^^^ -error[E0080]: evaluation of constant expression failed +error[E0080]: evaluation of constant value failed --> $DIR/conditional_array_execution.rs:11:20 | LL | println!("{}", FOO); diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.rs b/src/test/ui/consts/const-eval/const-eval-query-stack.rs index 39803c8f257e0..cbfeca2402666 100644 --- a/src/test/ui/consts/const-eval/const-eval-query-stack.rs +++ b/src/test/ui/consts/const-eval/const-eval-query-stack.rs @@ -21,6 +21,6 @@ const X: i32 = 1 / 0; //~WARN any use of this value will cause an error fn main() { let x: &'static i32 = &X; - //~^ ERROR evaluation of constant expression failed + //~^ ERROR evaluation of constant value failed println!("x={}", x); } diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr index 0016d301e598c..3e727b84aed10 100644 --- a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr +++ b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr @@ -12,13 +12,11 @@ note: the lint level is defined here LL | #[warn(const_err)] | ^^^^^^^^^ -error[E0080]: evaluation of constant expression failed - --> $DIR/const-eval-query-stack.rs:23:27 +error[E0080]: evaluation of constant value failed + --> $DIR/const-eval-query-stack.rs:23:28 | LL | let x: &'static i32 = &X; - | ^- - | | - | referenced constant has errors + | ^ referenced constant has errors query stack during panic: #0 [normalize_generic_arg_after_erasing_regions] normalizing `main::promoted[1]` #1 [optimized_mir] optimizing MIR for `main` diff --git a/src/test/ui/consts/const-eval/const_fn_ptr_fail2.rs b/src/test/ui/consts/const-eval/const_fn_ptr_fail2.rs index f67871e6142ef..0a2532973f423 100644 --- a/src/test/ui/consts/const-eval/const_fn_ptr_fail2.rs +++ b/src/test/ui/consts/const-eval/const_fn_ptr_fail2.rs @@ -18,7 +18,7 @@ const Z: usize = bar(double, 2); // FIXME: should fail to typeck someday fn main() { assert_eq!(Y, 4); - //~^ ERROR evaluation of constant expression failed + //~^ ERROR evaluation of constant value failed assert_eq!(Z, 4); - //~^ ERROR evaluation of constant expression failed + //~^ ERROR evaluation of constant value failed } diff --git a/src/test/ui/consts/const-eval/const_fn_ptr_fail2.stderr b/src/test/ui/consts/const-eval/const_fn_ptr_fail2.stderr index 822d4af83064e..2afedf30563a6 100644 --- a/src/test/ui/consts/const-eval/const_fn_ptr_fail2.stderr +++ b/src/test/ui/consts/const-eval/const_fn_ptr_fail2.stderr @@ -1,22 +1,14 @@ -error[E0080]: evaluation of constant expression failed - --> $DIR/const_fn_ptr_fail2.rs:20:5 +error[E0080]: evaluation of constant value failed + --> $DIR/const_fn_ptr_fail2.rs:20:16 | LL | assert_eq!(Y, 4); - | ^^^^^^^^^^^-^^^^^ - | | - | referenced constant has errors - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^ referenced constant has errors -error[E0080]: evaluation of constant expression failed - --> $DIR/const_fn_ptr_fail2.rs:22:5 +error[E0080]: evaluation of constant value failed + --> $DIR/const_fn_ptr_fail2.rs:22:16 | LL | assert_eq!(Z, 4); - | ^^^^^^^^^^^-^^^^^ - | | - | referenced constant has errors - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^ referenced constant has errors warning: skipping const checks | diff --git a/src/test/ui/consts/const-eval/issue-43197.rs b/src/test/ui/consts/const-eval/issue-43197.rs index 9109307632b59..7d1d33288a907 100644 --- a/src/test/ui/consts/const-eval/issue-43197.rs +++ b/src/test/ui/consts/const-eval/issue-43197.rs @@ -12,8 +12,8 @@ fn main() { const Y: u32 = foo(0 - 1); //~^ WARN any use of this value will cause println!("{} {}", X, Y); - //~^ ERROR evaluation of constant expression failed - //~| ERROR evaluation of constant expression failed + //~^ ERROR evaluation of constant value failed + //~| ERROR evaluation of constant value failed //~| WARN erroneous constant used [const_err] //~| WARN erroneous constant used [const_err] } diff --git a/src/test/ui/consts/const-eval/issue-43197.stderr b/src/test/ui/consts/const-eval/issue-43197.stderr index 27e067cedbb5c..8c72b59141687 100644 --- a/src/test/ui/consts/const-eval/issue-43197.stderr +++ b/src/test/ui/consts/const-eval/issue-43197.stderr @@ -20,7 +20,7 @@ LL | const Y: u32 = foo(0 - 1); | | | attempt to compute `0_u32 - 1_u32`, which would overflow -error[E0080]: evaluation of constant expression failed +error[E0080]: evaluation of constant value failed --> $DIR/issue-43197.rs:14:23 | LL | println!("{} {}", X, Y); @@ -32,7 +32,7 @@ warning: erroneous constant used LL | println!("{} {}", X, Y); | ^ referenced constant has errors -error[E0080]: evaluation of constant expression failed +error[E0080]: evaluation of constant value failed --> $DIR/issue-43197.rs:14:26 | LL | println!("{} {}", X, Y); diff --git a/src/test/ui/consts/const-eval/issue-44578.rs b/src/test/ui/consts/const-eval/issue-44578.rs index f9194709dc0b7..79f1301a2f944 100644 --- a/src/test/ui/consts/const-eval/issue-44578.rs +++ b/src/test/ui/consts/const-eval/issue-44578.rs @@ -25,5 +25,5 @@ impl Foo for u16 { fn main() { println!("{}", as Foo>::AMT); - //~^ ERROR evaluation of constant expression failed [E0080] + //~^ ERROR evaluation of constant value failed [E0080] } diff --git a/src/test/ui/consts/const-eval/issue-44578.stderr b/src/test/ui/consts/const-eval/issue-44578.stderr index f4323713e682b..bff9f40f82b35 100644 --- a/src/test/ui/consts/const-eval/issue-44578.stderr +++ b/src/test/ui/consts/const-eval/issue-44578.stderr @@ -1,4 +1,4 @@ -error[E0080]: evaluation of constant expression failed +error[E0080]: evaluation of constant value failed --> $DIR/issue-44578.rs:27:20 | LL | println!("{}", as Foo>::AMT); diff --git a/src/test/ui/consts/const-eval/issue-50814-2.stderr b/src/test/ui/consts/const-eval/issue-50814-2.stderr index ca8885e935090..f929f500cf9fb 100644 --- a/src/test/ui/consts/const-eval/issue-50814-2.stderr +++ b/src/test/ui/consts/const-eval/issue-50814-2.stderr @@ -8,13 +8,11 @@ LL | const BAR: usize = [5, 6, 7][T::BOO]; | = note: `#[deny(const_err)]` on by default -error[E0080]: evaluation of constant expression failed - --> $DIR/issue-50814-2.rs:18:5 +error[E0080]: evaluation of constant value failed + --> $DIR/issue-50814-2.rs:18:6 | LL | & as Foo>::BAR - | ^--------------------- - | | - | referenced constant has errors + | ^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const-eval/issue-50814.stderr b/src/test/ui/consts/const-eval/issue-50814.stderr index 7327138627648..307fb3c8c9de1 100644 --- a/src/test/ui/consts/const-eval/issue-50814.stderr +++ b/src/test/ui/consts/const-eval/issue-50814.stderr @@ -8,13 +8,11 @@ LL | const MAX: u8 = A::MAX + B::MAX; | = note: `#[deny(const_err)]` on by default -error[E0080]: evaluation of constant expression failed - --> $DIR/issue-50814.rs:20:5 +error[E0080]: evaluation of constant value failed + --> $DIR/issue-50814.rs:20:6 | LL | &Sum::::MAX - | ^----------------- - | | - | referenced constant has errors + | ^^^^^^^^^^^^^^^^^ referenced constant has errors error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const_unsafe_unreachable_ub.stderr b/src/test/ui/consts/const_unsafe_unreachable_ub.stderr index 31090be090833..6dddc7ff6e9d2 100644 --- a/src/test/ui/consts/const_unsafe_unreachable_ub.stderr +++ b/src/test/ui/consts/const_unsafe_unreachable_ub.stderr @@ -20,15 +20,11 @@ note: the lint level is defined here LL | #[warn(const_err)] | ^^^^^^^^^ -error[E0080]: evaluation of constant expression failed - --> $DIR/const_unsafe_unreachable_ub.rs:17:3 +error[E0080]: evaluation of constant value failed + --> $DIR/const_unsafe_unreachable_ub.rs:17:14 | LL | assert_eq!(BAR, true); - | ^^^^^^^^^^^---^^^^^^^^ - | | - | referenced constant has errors - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^ referenced constant has errors error: erroneous constant used --> $DIR/const_unsafe_unreachable_ub.rs:17:3 diff --git a/src/test/ui/consts/issue-55878.stderr b/src/test/ui/consts/issue-55878.stderr index 924910e9cb6df..ede5487b65d39 100644 --- a/src/test/ui/consts/issue-55878.stderr +++ b/src/test/ui/consts/issue-55878.stderr @@ -2,15 +2,12 @@ error[E0080]: values of the type `[u8; SIZE]` are too big for the current archit --> $SRC_DIR/core/src/mem/mod.rs:LL:COL | LL | intrinsics::size_of::() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | inside `std::mem::size_of::<[u8; SIZE]>` at $SRC_DIR/core/src/mem/mod.rs:LL:COL - | inside `main` at $DIR/issue-55878.rs:7:26 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ inside `std::mem::size_of::<[u8; SIZE]>` at $SRC_DIR/core/src/mem/mod.rs:LL:COL | ::: $DIR/issue-55878.rs:7:26 | LL | println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>()); - | ---------------------------------------------- + | ---------------------------------------------- inside `main` at $DIR/issue-55878.rs:7:26 error: erroneous constant used --> $DIR/issue-55878.rs:7:26 From 6d4e03af1e092b469825d998f6ea6b7e27def4d4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 24 Jan 2021 12:12:08 +0100 Subject: [PATCH 11/19] codegen: assume constants cannot fail to evaluate --- compiler/rustc_codegen_ssa/src/mir/mod.rs | 21 ++++++++++++++++ compiler/rustc_codegen_ssa/src/mir/operand.rs | 25 +++---------------- compiler/rustc_mir/src/interpret/operand.rs | 4 +++ 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 285140060be45..8a009b66b593e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -188,8 +188,10 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut bx); + let mut all_consts_ok = true; for const_ in &mir.required_consts { if let Err(err) = fx.eval_mir_constant(const_) { + all_consts_ok = false; match err { // errored or at least linted ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {} @@ -199,6 +201,25 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } } + if !all_consts_ok { + // There is no delay_span_bug here, we just have to rely on a hard error actually having + // been raised. + bx.abort(); + // `abort` does not terminate the block, so we still need to generate + // an `unreachable` terminator after it. + bx.unreachable(); + // We also have to delete all the other basic blocks again... + for bb in mir.basic_blocks().indices() { + if bb != mir::START_BLOCK { + unsafe { + bx.delete_basic_block(fx.blocks[bb]); + } + } + } + // FIXME: Can we somehow avoid all this by evaluating the consts before creating the basic + // blocks? The tricky part is doing that without duplicating `fx.eval_mir_constant`. + return; + } let memory_locals = analyze::non_ssa_locals(&fx); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 08a4ae3962bee..25e84c38ed315 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -6,9 +6,8 @@ use crate::glue; use crate::traits::*; use crate::MemFlags; -use rustc_errors::ErrorReported; use rustc_middle::mir; -use rustc_middle::mir::interpret::{ConstValue, ErrorHandled, Pointer, Scalar}; +use rustc_middle::mir::interpret::{ConstValue, Pointer, Scalar}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::Ty; use rustc_target::abi::{Abi, Align, LayoutOf, Size}; @@ -439,25 +438,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::Operand::Constant(ref constant) => { - self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|err| { - match err { - // errored or at least linted - ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {} - ErrorHandled::TooGeneric => { - bug!("codegen encountered polymorphic constant") - } - } - // Allow RalfJ to sleep soundly knowing that even refactorings that remove - // the above error (or silence it under some conditions) will not cause UB. - bx.abort(); - // We still have to return an operand but it doesn't matter, - // this code is unreachable. - let ty = self.monomorphize(constant.literal.ty); - let layout = bx.cx().layout_of(ty); - bx.load_operand(PlaceRef::new_sized( - bx.cx().const_undef(bx.cx().type_ptr_to(bx.cx().backend_type(layout))), - layout, - )) + // This cannot fail because we checked all required_consts in advance. + self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|_err| { + span_bug!(constant.span, "erroneous constant not captured by required_consts") }) } } diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index d9437a312aec0..88236458a213a 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -511,6 +511,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Constant(ref constant) => { let val = self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal); + // This can still fail: + // * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all + // checked yet. + // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail. self.const_to_op(val, layout)? } }; From 2be19932335f76cb4a97c275038a019751fbba6a Mon Sep 17 00:00:00 2001 From: Camelid Date: Sun, 24 Jan 2021 19:38:10 -0800 Subject: [PATCH 12/19] Ignore test on 32-bit architectures --- src/test/ui/consts/issue-79690.rs | 3 +++ src/test/ui/consts/issue-79690.stderr | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/ui/consts/issue-79690.rs b/src/test/ui/consts/issue-79690.rs index 6a38b3c8c2c71..a2e7b97b3187d 100644 --- a/src/test/ui/consts/issue-79690.rs +++ b/src/test/ui/consts/issue-79690.rs @@ -1,3 +1,6 @@ +// ignore-32bit +// This test gives a different error on 32-bit architectures. + union Transmute { t: T, u: U, diff --git a/src/test/ui/consts/issue-79690.stderr b/src/test/ui/consts/issue-79690.stderr index c7f9c6a55e712..918dd4c20f96c 100644 --- a/src/test/ui/consts/issue-79690.stderr +++ b/src/test/ui/consts/issue-79690.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/issue-79690.rs:26:1 + --> $DIR/issue-79690.rs:29:1 | LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered read of part of a pointer at .1..size.foo From 59195a277246ec194ca3a02ecc563c4dd9c06857 Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Sun, 24 Jan 2021 21:23:38 -0800 Subject: [PATCH 13/19] rustc_codegen_ssa: use wall time for codegen_to_LLVM_IR time-passes entry Use elapsed wall time spent on codegen_to_LLVM_IR for all CGUs as a whole, rather than the sum for each CGU (the distinction matters for parallel builds, where some CGUs are processed in parallel). --- compiler/rustc_codegen_ssa/src/base.rs | 31 ++++++++++++-------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 2ce5fe5ad504b..ad72dc0086b61 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -13,7 +13,7 @@ use crate::{CachedModuleCodegen, CrateInfo, MemFlags, ModuleCodegen, ModuleKind} use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::print_time_passes_entry; -use rustc_data_structures::sync::{par_iter, Lock, ParallelIterator}; +use rustc_data_structures::sync::{par_iter, ParallelIterator}; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; @@ -554,8 +554,6 @@ pub fn codegen_crate( codegen_units }; - let total_codegen_time = Lock::new(Duration::new(0, 0)); - // The non-parallel compiler can only translate codegen units to LLVM IR // on a single thread, leading to a staircase effect where the N LLVM // threads have to wait on the single codegen threads to generate work @@ -578,23 +576,25 @@ pub fn codegen_crate( .collect(); // Compile the found CGUs in parallel. - par_iter(cgus) + let start_time = Instant::now(); + + let pre_compiled_cgus = par_iter(cgus) .map(|(i, _)| { - let start_time = Instant::now(); let module = backend.compile_codegen_unit(tcx, codegen_units[i].name()); - let mut time = total_codegen_time.lock(); - *time += start_time.elapsed(); (i, module) }) - .collect() + .collect(); + + (pre_compiled_cgus, start_time.elapsed()) }) } else { - FxHashMap::default() + (FxHashMap::default(), Duration::new(0, 0)) } }; let mut cgu_reuse = Vec::new(); let mut pre_compiled_cgus: Option> = None; + let mut total_codegen_time = Duration::new(0, 0); for (i, cgu) in codegen_units.iter().enumerate() { ongoing_codegen.wait_for_signal_to_codegen_item(); @@ -607,7 +607,9 @@ pub fn codegen_crate( codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect() }); // Pre compile some CGUs - pre_compiled_cgus = Some(pre_compile_cgus(&cgu_reuse)); + let (compiled_cgus, codegen_time) = pre_compile_cgus(&cgu_reuse); + pre_compiled_cgus = Some(compiled_cgus); + total_codegen_time += codegen_time; } let cgu_reuse = cgu_reuse[i]; @@ -621,8 +623,7 @@ pub fn codegen_crate( } else { let start_time = Instant::now(); let module = backend.compile_codegen_unit(tcx, cgu.name()); - let mut time = total_codegen_time.lock(); - *time += start_time.elapsed(); + total_codegen_time += start_time.elapsed(); module }; submit_codegened_module_to_llvm( @@ -663,11 +664,7 @@ pub fn codegen_crate( // Since the main thread is sometimes blocked during codegen, we keep track // -Ztime-passes output manually. - print_time_passes_entry( - tcx.sess.time_passes(), - "codegen_to_LLVM_IR", - total_codegen_time.into_inner(), - ); + print_time_passes_entry(tcx.sess.time_passes(), "codegen_to_LLVM_IR", total_codegen_time); ongoing_codegen.check_for_errors(tcx.sess); From 26b4baf46ea66d3651281c8b66d76853e6362c65 Mon Sep 17 00:00:00 2001 From: 1000teslas <47207223+1000teslas@users.noreply.github.com> Date: Mon, 18 Jan 2021 19:04:55 +1100 Subject: [PATCH 14/19] Point to span of upvar making closure FnMut Add expected error Add comment Tweak comment wording Fix after rebase to updated master Fix after rebase to updated master Distinguish mutation in normal and move closures Tweak error message Fix error message for nested closures Refactor code showing mutated upvar in closure Remove debug assert B --- .../diagnostics/mutability_errors.rs | 56 ++++++++++++++++++- .../borrow-raw-address-of-mutability.stderr | 4 +- .../issue-80313-mutable-borrow-in-closure.rs | 7 +++ ...sue-80313-mutable-borrow-in-closure.stderr | 14 +++++ ...ue-80313-mutable-borrow-in-move-closure.rs | 7 +++ ...0313-mutable-borrow-in-move-closure.stderr | 14 +++++ .../issue-80313-mutation-in-closure.rs | 7 +++ .../issue-80313-mutation-in-closure.stderr | 14 +++++ .../issue-80313-mutation-in-move-closure.rs | 7 +++ ...ssue-80313-mutation-in-move-closure.stderr | 14 +++++ ...es-infer-fnmut-calling-fnmut-no-mut.stderr | 4 ++ ...ed-closures-infer-fnmut-missing-mut.stderr | 4 +- ...osures-infer-fnmut-move-missing-mut.stderr | 4 +- 13 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs create mode 100644 src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr create mode 100644 src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs create mode 100644 src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr create mode 100644 src/test/ui/closures/issue-80313-mutation-in-closure.rs create mode 100644 src/test/ui/closures/issue-80313-mutation-in-closure.stderr create mode 100644 src/test/ui/closures/issue-80313-mutation-in-move-closure.rs create mode 100644 src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index e1af6fc07cf8f..73196c732f5bb 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -1,9 +1,12 @@ use rustc_hir as hir; use rustc_hir::Node; use rustc_index::vec::Idx; -use rustc_middle::mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location}; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{ + hir::place::PlaceBase, + mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location}, +}; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; @@ -241,6 +244,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { format!("mut {}", self.local_names[local].unwrap()), Applicability::MachineApplicable, ); + let tcx = self.infcx.tcx; + if let ty::Closure(id, _) = the_place_err.ty(self.body, tcx).ty.kind() { + self.show_mutating_upvar(tcx, id, the_place_err, &mut err); + } } // Also suggest adding mut for upvars @@ -271,6 +278,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); } } + + let tcx = self.infcx.tcx; + if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind() + { + if let ty::Closure(id, _) = ty.kind() { + self.show_mutating_upvar(tcx, id, the_place_err, &mut err); + } + } } // complete hack to approximate old AST-borrowck @@ -463,6 +478,45 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.buffer(&mut self.errors_buffer); } + // point to span of upvar making closure call require mutable borrow + fn show_mutating_upvar( + &self, + tcx: TyCtxt<'_>, + id: &hir::def_id::DefId, + the_place_err: PlaceRef<'tcx>, + err: &mut DiagnosticBuilder<'_>, + ) { + let id = id.expect_local(); + let tables = tcx.typeck(id); + let hir_id = tcx.hir().local_def_id_to_hir_id(id); + let (span, place) = &tables.closure_kind_origins()[hir_id]; + let reason = if let PlaceBase::Upvar(upvar_id) = place.base { + let upvar = ty::place_to_string_for_capture(tcx, place); + match tables.upvar_capture(upvar_id) { + ty::UpvarCapture::ByRef(ty::UpvarBorrow { + kind: ty::BorrowKind::MutBorrow, + .. + }) => { + format!("mutable borrow of `{}`", upvar) + } + ty::UpvarCapture::ByValue(_) => { + format!("possible mutation of `{}`", upvar) + } + _ => bug!("upvar `{}` borrowed, but not mutably", upvar), + } + } else { + bug!("not an upvar") + }; + err.span_label( + *span, + format!( + "calling `{}` requires mutable binding due to {}", + self.describe_place(the_place_err).unwrap(), + reason + ), + ); + } + /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected. fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) { err.span_label(sp, format!("cannot {}", act)); diff --git a/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr b/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr index 44dde0fd80b0d..ea74fb966846f 100644 --- a/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr +++ b/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr @@ -20,7 +20,9 @@ error[E0596]: cannot borrow `f` as mutable, as it is not declared as mutable | LL | let f = || { | - help: consider changing this to be mutable: `mut f` -... +LL | let y = &raw mut x; + | - calling `f` requires mutable binding due to mutable borrow of `x` +LL | }; LL | f(); | ^ cannot borrow as mutable diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs new file mode 100644 index 0000000000000..ff210ae06a3bd --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs @@ -0,0 +1,7 @@ +fn main() { + let mut my_var = false; + let callback = || { + &mut my_var; + }; + callback(); //~ ERROR E0596 +} diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr new file mode 100644 index 0000000000000..bf9e1febdbba4 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr @@ -0,0 +1,14 @@ +error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable + --> $DIR/issue-80313-mutable-borrow-in-closure.rs:6:5 + | +LL | let callback = || { + | -------- help: consider changing this to be mutable: `mut callback` +LL | &mut my_var; + | ------ calling `callback` requires mutable binding due to mutable borrow of `my_var` +LL | }; +LL | callback(); + | ^^^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs new file mode 100644 index 0000000000000..8f2d8a676302c --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs @@ -0,0 +1,7 @@ +fn main() { + let mut my_var = false; + let callback = move || { + &mut my_var; + }; + callback(); //~ ERROR E0596 +} diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr new file mode 100644 index 0000000000000..b67cec6a609f0 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr @@ -0,0 +1,14 @@ +error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable + --> $DIR/issue-80313-mutable-borrow-in-move-closure.rs:6:5 + | +LL | let callback = move || { + | -------- help: consider changing this to be mutable: `mut callback` +LL | &mut my_var; + | ------ calling `callback` requires mutable binding due to possible mutation of `my_var` +LL | }; +LL | callback(); + | ^^^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-80313-mutation-in-closure.rs b/src/test/ui/closures/issue-80313-mutation-in-closure.rs new file mode 100644 index 0000000000000..e082ea562ef22 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutation-in-closure.rs @@ -0,0 +1,7 @@ +fn main() { + let mut my_var = false; + let callback = || { + my_var = true; + }; + callback(); //~ ERROR E0596 +} diff --git a/src/test/ui/closures/issue-80313-mutation-in-closure.stderr b/src/test/ui/closures/issue-80313-mutation-in-closure.stderr new file mode 100644 index 0000000000000..6e98549f6b84f --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutation-in-closure.stderr @@ -0,0 +1,14 @@ +error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable + --> $DIR/issue-80313-mutation-in-closure.rs:6:5 + | +LL | let callback = || { + | -------- help: consider changing this to be mutable: `mut callback` +LL | my_var = true; + | ------ calling `callback` requires mutable binding due to mutable borrow of `my_var` +LL | }; +LL | callback(); + | ^^^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-80313-mutation-in-move-closure.rs b/src/test/ui/closures/issue-80313-mutation-in-move-closure.rs new file mode 100644 index 0000000000000..f66bf4e062831 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutation-in-move-closure.rs @@ -0,0 +1,7 @@ +fn main() { + let mut my_var = false; + let callback = move || { + my_var = true; + }; + callback(); //~ ERROR E0596 +} diff --git a/src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr b/src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr new file mode 100644 index 0000000000000..edd55422a0bd4 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr @@ -0,0 +1,14 @@ +error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable + --> $DIR/issue-80313-mutation-in-move-closure.rs:6:5 + | +LL | let callback = move || { + | -------- help: consider changing this to be mutable: `mut callback` +LL | my_var = true; + | ------ calling `callback` requires mutable binding due to possible mutation of `my_var` +LL | }; +LL | callback(); + | ^^^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr index 5dea424596e9c..a0ed56d4bcf7b 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr @@ -3,6 +3,8 @@ error[E0596]: cannot borrow `tick1` as mutable, as it is not declared as mutable | LL | let tick1 = || { | ----- help: consider changing this to be mutable: `mut tick1` +LL | counter += 1; + | ------- calling `tick1` requires mutable binding due to mutable borrow of `counter` ... LL | tick1(); | ^^^^^ cannot borrow as mutable @@ -12,6 +14,8 @@ error[E0596]: cannot borrow `tick2` as mutable, as it is not declared as mutable | LL | let tick2 = || { | ----- help: consider changing this to be mutable: `mut tick2` +LL | tick1(); + | ----- calling `tick2` requires mutable binding due to mutable borrow of `tick1` ... LL | tick2(); | ^^^^^ cannot borrow as mutable diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr index eb398628846dd..27d23e3fa044b 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr @@ -2,7 +2,9 @@ error[E0596]: cannot borrow `tick` as mutable, as it is not declared as mutable --> $DIR/unboxed-closures-infer-fnmut-missing-mut.rs:7:5 | LL | let tick = || counter += 1; - | ---- help: consider changing this to be mutable: `mut tick` + | ---- ------- calling `tick` requires mutable binding due to mutable borrow of `counter` + | | + | help: consider changing this to be mutable: `mut tick` LL | tick(); | ^^^^ cannot borrow as mutable diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr index b9d76d9a752ce..c00f986c397a7 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr @@ -2,7 +2,9 @@ error[E0596]: cannot borrow `tick` as mutable, as it is not declared as mutable --> $DIR/unboxed-closures-infer-fnmut-move-missing-mut.rs:7:5 | LL | let tick = move || counter += 1; - | ---- help: consider changing this to be mutable: `mut tick` + | ---- ------- calling `tick` requires mutable binding due to possible mutation of `counter` + | | + | help: consider changing this to be mutable: `mut tick` LL | tick(); | ^^^^ cannot borrow as mutable From 088c89d9ffe47fb6d1b51c8edfd8ad652ee2a7f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 19 Jan 2021 12:44:49 -0800 Subject: [PATCH 15/19] Account for generics when suggesting bound Fix #81175. --- .../src/traits/error_reporting/suggestions.rs | 35 +++++--- src/test/ui/bound-suggestions.fixed | 27 +++++- src/test/ui/bound-suggestions.rs | 27 +++++- src/test/ui/bound-suggestions.stderr | 82 ++++++++++++++++++- 4 files changed, 156 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 1830aaa4471a6..0ab00fddfafc8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -286,21 +286,32 @@ fn suggest_restriction( ); } else { // Trivial case: `T` needs an extra bound: `T: Bound`. - let (sp, suggestion) = match super_traits { - None => predicate_constraint( + let (sp, suggestion) = match ( + generics + .params + .iter() + .filter( + |p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: Some(_), ..}), + ) + .next(), + super_traits, + ) { + (_, None) => predicate_constraint( generics, trait_ref.without_const().to_predicate(tcx).to_string(), ), - Some((ident, bounds)) => match bounds { - [.., bound] => ( - bound.span().shrink_to_hi(), - format!(" + {}", trait_ref.print_only_trait_path().to_string()), - ), - [] => ( - ident.span.shrink_to_hi(), - format!(": {}", trait_ref.print_only_trait_path().to_string()), - ), - }, + (None, Some((ident, []))) => ( + ident.span.shrink_to_hi(), + format!(": {}", trait_ref.print_only_trait_path().to_string()), + ), + (_, Some((_, [.., bounds]))) => ( + bounds.span().shrink_to_hi(), + format!(" + {}", trait_ref.print_only_trait_path().to_string()), + ), + (Some(_), Some((_, []))) => ( + generics.span.shrink_to_hi(), + format!(": {}", trait_ref.print_only_trait_path().to_string()), + ), }; err.span_suggestion_verbose( diff --git a/src/test/ui/bound-suggestions.fixed b/src/test/ui/bound-suggestions.fixed index a3fe67a95954f..be61b7dda256a 100644 --- a/src/test/ui/bound-suggestions.fixed +++ b/src/test/ui/bound-suggestions.fixed @@ -40,4 +40,29 @@ fn test_many_bounds_where(x: X) where X: Sized, X: Sized, X: Debug { //~^ ERROR doesn't implement } -pub fn main() { } +trait Foo: Sized { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +trait Bar: std::fmt::Display + Sized { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +trait Baz: Sized where Self: std::fmt::Display { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +trait Qux: Sized where Self: std::fmt::Display { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +trait Bat: std::fmt::Display + Sized { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +fn main() { } diff --git a/src/test/ui/bound-suggestions.rs b/src/test/ui/bound-suggestions.rs index de6133d7f59ac..86f708d42f5e7 100644 --- a/src/test/ui/bound-suggestions.rs +++ b/src/test/ui/bound-suggestions.rs @@ -40,4 +40,29 @@ fn test_many_bounds_where(x: X) where X: Sized, X: Sized { //~^ ERROR doesn't implement } -pub fn main() { } +trait Foo { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +trait Bar: std::fmt::Display { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +trait Baz where Self: std::fmt::Display { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +trait Qux where Self: std::fmt::Display { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +trait Bat: std::fmt::Display { + const SIZE: usize = core::mem::size_of::(); + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +fn main() { } diff --git a/src/test/ui/bound-suggestions.stderr b/src/test/ui/bound-suggestions.stderr index 010f95d8ad6f0..12e67e90265ab 100644 --- a/src/test/ui/bound-suggestions.stderr +++ b/src/test/ui/bound-suggestions.stderr @@ -76,6 +76,86 @@ help: consider further restricting type parameter `X` LL | fn test_many_bounds_where(x: X) where X: Sized, X: Sized, X: Debug { | ^^^^^^^^^^ -error: aborting due to 6 previous errors +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/bound-suggestions.rs:44:46 + | +LL | const SIZE: usize = core::mem::size_of::(); + | ^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/core/src/mem/mod.rs:LL:COL + | +LL | pub const fn size_of() -> usize { + | - required by this bound in `std::mem::size_of` + | +help: consider further restricting `Self` + | +LL | trait Foo: Sized { + | ^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/bound-suggestions.rs:49:46 + | +LL | const SIZE: usize = core::mem::size_of::(); + | ^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/core/src/mem/mod.rs:LL:COL + | +LL | pub const fn size_of() -> usize { + | - required by this bound in `std::mem::size_of` + | +help: consider further restricting `Self` + | +LL | trait Bar: std::fmt::Display + Sized { + | ^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/bound-suggestions.rs:54:46 + | +LL | const SIZE: usize = core::mem::size_of::(); + | ^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/core/src/mem/mod.rs:LL:COL + | +LL | pub const fn size_of() -> usize { + | - required by this bound in `std::mem::size_of` + | +help: consider further restricting `Self` + | +LL | trait Baz: Sized where Self: std::fmt::Display { + | ^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/bound-suggestions.rs:59:46 + | +LL | const SIZE: usize = core::mem::size_of::(); + | ^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/core/src/mem/mod.rs:LL:COL + | +LL | pub const fn size_of() -> usize { + | - required by this bound in `std::mem::size_of` + | +help: consider further restricting `Self` + | +LL | trait Qux: Sized where Self: std::fmt::Display { + | ^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/bound-suggestions.rs:64:46 + | +LL | const SIZE: usize = core::mem::size_of::(); + | ^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/core/src/mem/mod.rs:LL:COL + | +LL | pub const fn size_of() -> usize { + | - required by this bound in `std::mem::size_of` + | +help: consider further restricting `Self` + | +LL | trait Bat: std::fmt::Display + Sized { + | ^^^^^^^ + +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0277`. From 042facb9357f9b6248b0b2b6c5624742c3948e47 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Jan 2021 14:55:24 +0100 Subject: [PATCH 16/19] Fix some bugs reported by eslint --- src/librustdoc/html/static/main.js | 50 +++++++++------------ src/librustdoc/html/static/settings.js | 2 +- src/librustdoc/html/static/source-script.js | 2 + src/librustdoc/html/static/storage.js | 10 ++++- 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 74bd348e9ac1c..53f0831852456 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -1,9 +1,6 @@ -// From rust: -/* global ALIASES */ - // Local js definitions: -/* global addClass, getCurrentValue, hasClass */ -/* global onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */ +/* global addClass, getSettingValue, hasClass */ +/* global onEach, onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */ /* global hideThemeButtonState, showThemeButtonState */ if (!String.prototype.startsWith) { @@ -2214,7 +2211,7 @@ function defocusSearchBar() { } } - function toggleAllDocs(pageId, fromAutoCollapse) { + function toggleAllDocs(fromAutoCollapse) { var innerToggle = document.getElementById(toggleAllDocsId); if (!innerToggle) { return; @@ -2257,14 +2254,14 @@ function defocusSearchBar() { } if (!parent || !superParent || superParent.id !== "main" || hasClass(parent, "impl") === false) { - collapseDocs(e, "hide", pageId); + collapseDocs(e, "hide"); } }); } } } - function collapseDocs(toggle, mode, pageId) { + function collapseDocs(toggle, mode) { if (!toggle || !toggle.parentNode) { return; } @@ -2384,27 +2381,27 @@ function defocusSearchBar() { } } - function collapser(pageId, e, collapse) { + function collapser(e, collapse) { // inherent impl ids are like "impl" or impl-'. // they will never be hidden by default. var n = e.parentElement; if (n.id.match(/^impl(?:-\d+)?$/) === null) { // Automatically minimize all non-inherent impls if (collapse || hasClass(n, "impl")) { - collapseDocs(e, "hide", pageId); + collapseDocs(e, "hide"); } } } - function autoCollapse(pageId, collapse) { + function autoCollapse(collapse) { if (collapse) { - toggleAllDocs(pageId, true); + toggleAllDocs(true); } else if (getSettingValue("auto-hide-trait-implementations") !== "false") { var impl_list = document.getElementById("trait-implementations-list"); if (impl_list !== null) { onEachLazy(impl_list.getElementsByClassName("collapse-toggle"), function(e) { - collapser(pageId, e, collapse); + collapser(e, collapse); }); } @@ -2412,7 +2409,7 @@ function defocusSearchBar() { if (blanket_list !== null) { onEachLazy(blanket_list.getElementsByClassName("collapse-toggle"), function(e) { - collapser(pageId, e, collapse); + collapser(e, collapse); }); } } @@ -2475,7 +2472,6 @@ function defocusSearchBar() { var toggle = createSimpleToggle(false); var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true"; var hideImplementors = getSettingValue("auto-collapse-implementors") !== "false"; - var pageId = getPageId(); var func = function(e) { var next = e.nextElementSibling; @@ -2489,7 +2485,7 @@ function defocusSearchBar() { var newToggle = toggle.cloneNode(true); insertAfter(newToggle, e.childNodes[e.childNodes.length - 1]); if (hideMethodDocs === true && hasClass(e, "method") === true) { - collapseDocs(newToggle, "hide", pageId); + collapseDocs(newToggle, "hide"); } } }; @@ -2513,7 +2509,7 @@ function defocusSearchBar() { // In case the option "auto-collapse implementors" is not set to false, we collapse // all implementors. if (hideImplementors === true && e.parentNode.id === "implementors-list") { - collapseDocs(newToggle, "hide", pageId); + collapseDocs(newToggle, "hide"); } } }; @@ -2527,7 +2523,7 @@ function defocusSearchBar() { if (e.id.match(/^impl(?:-\d+)?$/) === null) { // Automatically minimize all non-inherent impls if (hasClass(e, "impl") === true) { - collapseDocs(newToggle, "hide", pageId); + collapseDocs(newToggle, "hide"); } } }; @@ -2562,14 +2558,12 @@ function defocusSearchBar() { } onEachLazy(document.getElementsByClassName("impl-items"), function(e) { onEachLazy(e.getElementsByClassName("associatedconstant"), func); - var hiddenElems = e.getElementsByClassName("hidden"); - var needToggle = false; - - var needToggle = onEachLazy(e.getElementsByClassName("hidden"), function(hiddenElem) { - if (hasClass(hiddenElem, "content") === false && - hasClass(hiddenElem, "docblock") === false) { - return true; - } + // We transform the DOM iterator into a vec of DOM elements to prevent performance + // issues on webkit browsers. + var hiddenElems = Array.prototype.slice.call(e.getElementsByClassName("hidden")); + var needToggle = hiddenElems.some(function(hiddenElem) { + return hasClass(hiddenElem, "content") === false && + hasClass(hiddenElem, "docblock") === false; }); if (needToggle === true) { var inner_toggle = newToggle.cloneNode(true); @@ -2672,10 +2666,10 @@ function defocusSearchBar() { onEachLazy(document.getElementsByClassName("docblock"), buildToggleWrapper); onEachLazy(document.getElementsByClassName("sub-variant"), buildToggleWrapper); - var pageId = getPageId(); - autoCollapse(pageId, getSettingValue("collapse") === "true"); + autoCollapse(getSettingValue("collapse") === "true"); + var pageId = getPageId(); if (pageId !== null) { expandSection(pageId); } diff --git a/src/librustdoc/html/static/settings.js b/src/librustdoc/html/static/settings.js index bc14420232c4d..4f10e14e8558c 100644 --- a/src/librustdoc/html/static/settings.js +++ b/src/librustdoc/html/static/settings.js @@ -1,5 +1,5 @@ // Local js definitions: -/* global getCurrentValue, getVirtualKey, updateLocalStorage, updateSystemTheme */ +/* global getSettingValue, getVirtualKey, onEachLazy, updateLocalStorage, updateSystemTheme */ (function () { function changeSetting(settingName, value) { diff --git a/src/librustdoc/html/static/source-script.js b/src/librustdoc/html/static/source-script.js index a9cc0ffdf79b0..42b54e4cc1e46 100644 --- a/src/librustdoc/html/static/source-script.js +++ b/src/librustdoc/html/static/source-script.js @@ -113,6 +113,8 @@ function createSidebarToggle() { return sidebarToggle; } +// This function is called from "source-files.js", generated in `html/render/mod.rs`. +// eslint-disable-next-line no-unused-vars function createSourceSidebar() { if (window.rootPath.endsWith("/") === false) { window.rootPath += "/"; diff --git a/src/librustdoc/html/static/storage.js b/src/librustdoc/html/static/storage.js index d081781f14be1..9c5ac1625afea 100644 --- a/src/librustdoc/html/static/storage.js +++ b/src/librustdoc/html/static/storage.js @@ -1,5 +1,5 @@ // From rust: -/* global resourcesSuffix, getSettingValue */ +/* global resourcesSuffix */ var darkThemes = ["dark", "ayu"]; var currentTheme = document.getElementById("themeStyle"); @@ -35,10 +35,12 @@ var localStoredTheme = getSettingValue("theme"); var savedHref = []; +// eslint-disable-next-line no-unused-vars function hasClass(elem, className) { return elem && elem.classList && elem.classList.contains(className); } +// eslint-disable-next-line no-unused-vars function addClass(elem, className) { if (!elem || !elem.classList) { return; @@ -46,6 +48,7 @@ function addClass(elem, className) { elem.classList.add(className); } +// eslint-disable-next-line no-unused-vars function removeClass(elem, className) { if (!elem || !elem.classList) { return; @@ -81,6 +84,7 @@ function onEachLazy(lazyArray, func, reversed) { reversed); } +// eslint-disable-next-line no-unused-vars function hasOwnProperty(obj, property) { return Object.prototype.hasOwnProperty.call(obj, property); } @@ -148,6 +152,8 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) { } } +// This function is called from "theme.js", generated in `html/render/mod.rs`. +// eslint-disable-next-line no-unused-vars function useSystemTheme(value) { if (value === undefined) { value = true; @@ -172,7 +178,7 @@ var updateSystemTheme = (function() { switchTheme( currentTheme, mainTheme, - JSON.parse(cssTheme) || light, + JSON.parse(cssTheme) || "light", true ); }; From 0140dacabbc6b5a33f19f88c22b531f318ef8f37 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 25 Jan 2021 14:53:19 +0100 Subject: [PATCH 17/19] Link the reference about undefined behavior Suggested-by: Mara Bos Signed-off-by: Miguel Ojeda --- library/core/src/option.rs | 4 +++- library/core/src/result.rs | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 5d34f5ca155bf..9f89bfd674a74 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -433,7 +433,9 @@ impl Option { /// /// # Safety /// - /// Undefined behavior if the value is [`None`]. + /// Calling this method on [`None`] is *[undefined behavior]*. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples /// diff --git a/library/core/src/result.rs b/library/core/src/result.rs index a357750b92f9d..436f4bf20c7e7 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -827,7 +827,9 @@ impl Result { /// /// # Safety /// - /// Undefined behavior if the value is an [`Err`]. + /// Calling this method on an [`Err`] is *[undefined behavior]*. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples /// @@ -859,7 +861,9 @@ impl Result { /// /// # Safety /// - /// Undefined behavior if the value is an [`Ok`]. + /// Calling this method on an [`Ok`] is *[undefined behavior]*. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples /// From 01250fcec6c77552b0f7aac11ed833412294ccba Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 25 Jan 2021 14:58:09 +0100 Subject: [PATCH 18/19] Add tracking issue Signed-off-by: Miguel Ojeda --- library/core/src/option.rs | 2 +- library/core/src/result.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 9f89bfd674a74..14e4e4da3b96d 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -452,7 +452,7 @@ impl Option { /// ``` #[inline] #[track_caller] - #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")] pub unsafe fn unwrap_unchecked(self) -> T { debug_assert!(self.is_some()); match self { diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 436f4bf20c7e7..a43ba5882edcd 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -846,7 +846,7 @@ impl Result { /// ``` #[inline] #[track_caller] - #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")] pub unsafe fn unwrap_unchecked(self) -> T { debug_assert!(self.is_ok()); match self { @@ -880,7 +880,7 @@ impl Result { /// ``` #[inline] #[track_caller] - #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "none")] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")] pub unsafe fn unwrap_err_unchecked(self) -> E { debug_assert!(self.is_err()); match self { From 1c0a52d304cdebfcdf57c528b413457030659eb0 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 25 Jan 2021 11:00:02 -0800 Subject: [PATCH 19/19] rustdoc: Document CommonMark extensions. --- .../rustdoc/src/how-to-write-documentation.md | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/doc/rustdoc/src/how-to-write-documentation.md b/src/doc/rustdoc/src/how-to-write-documentation.md index ca6db26da3130..41736e5ee3a7e 100644 --- a/src/doc/rustdoc/src/how-to-write-documentation.md +++ b/src/doc/rustdoc/src/how-to-write-documentation.md @@ -153,11 +153,73 @@ and finally provides a code example. ## Markdown -`rustdoc` uses the [commonmark markdown specification]. You might be +`rustdoc` uses the [CommonMark markdown specification]. You might be interested into taking a look at their website to see what's possible to do. - [commonmark quick reference] - [current spec] +In addition to the standard CommonMark syntax, `rustdoc` supports several +extensions: + +### Strikethrough + +Text may be rendered with a horizontal line through the center by wrapping the +text with two tilde characters on each side: + +```text +An example of ~~strikethrough text~~. +``` + +This example will render as: + +> An example of ~~strikethrough text~~. + +This follows the [GitHub Strikethrough extension][strikethrough]. + +### Footnotes + +A footnote generates a small numbered link in the text which when clicked +takes the reader to the footnote text at the bottom of the item. The footnote +label is written similarly to a link reference with a caret at the front. The +footnote text is written like a link reference definition, with the text +following the label. Example: + +```text +This is an example of a footnote[^note]. + +[^note]: This text is the contents of the footnote, which will be rendered + towards the bottom. +``` + +This example will render as: + +> This is an example of a footnote[^note]. +> +> [^note]: This text is the contents of the footnote, which will be rendered +> towards the bottom. + +The footnotes are automatically numbered based on the order the footnotes are +written. + +### Tables + +Tables can be written using pipes and dashes to draw the rows and columns of +the table. These will be translated to HTML table matching the shape. Example: + +```text +| Header1 | Header2 | +|---------|---------| +| abc | def | +``` + +This example will render similarly to this: + +> | Header1 | Header2 | +> |---------|---------| +> | abc | def | + +See the specification for the [GitHub Tables extension][tables] for more +details on the exact syntax supported. [`backtrace`]: https://docs.rs/backtrace/0.3.50/backtrace/ [commonmark markdown specification]: https://commonmark.org/ @@ -170,3 +232,5 @@ interested into taking a look at their website to see what's possible to do. [standard library]: https://doc.rust-lang.org/stable/std/index.html [current spec]: https://spec.commonmark.org/current/ [`std::env`]: https://doc.rust-lang.org/stable/std/env/index.html#functions +[strikethrough]: https://github.github.com/gfm/#strikethrough-extension- +[tables]: https://github.github.com/gfm/#tables-extension-