From 2538c0c17095834c994cbfdc4909338a31f83fb7 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Tue, 10 Jan 2023 21:39:02 -0500 Subject: [PATCH 1/9] fix `SyncSender` spinning behavior --- library/std/src/sync/mpmc/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs index e030c55ce8f61..a9b365daeec4e 100644 --- a/library/std/src/sync/mpmc/utils.rs +++ b/library/std/src/sync/mpmc/utils.rs @@ -139,6 +139,6 @@ impl Backoff { /// Returns `true` if quadratic backoff has completed and blocking the thread is advised. #[inline] pub fn is_completed(&self) -> bool { - self.step.get() > YIELD_LIMIT + self.step.get() > SPIN_LIMIT } } From f8276c94ac06b272e88fb1bb9c5f6615fc5876ef Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Tue, 10 Jan 2023 21:54:53 -0500 Subject: [PATCH 2/9] add `SyncSender::send_timeout` test --- library/std/src/sync/mpmc/mod.rs | 2 +- library/std/src/sync/mpsc/mod.rs | 9 +++++++++ library/std/src/sync/mpsc/sync_tests.rs | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index cef99c5884300..7a602cecd3b89 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -43,7 +43,7 @@ mod zero; use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::time::{Duration, Instant}; -use error::*; +pub use error::*; /// Creates a channel of unbounded capacity. /// diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index adb488d4378f0..6e3c28f10bb1b 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -738,6 +738,15 @@ impl SyncSender { pub fn try_send(&self, t: T) -> Result<(), TrySendError> { self.inner.try_send(t) } + + // Attempts to send for a value on this receiver, returning an error if the + // corresponding channel has hung up, or if it waits more than `timeout`. + // + // This method is currently private and only used for tests. + #[allow(unused)] + fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError> { + self.inner.send_timeout(t, timeout) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/src/sync/mpsc/sync_tests.rs index 63c79436974d5..9d2f92ffc9b14 100644 --- a/library/std/src/sync/mpsc/sync_tests.rs +++ b/library/std/src/sync/mpsc/sync_tests.rs @@ -1,5 +1,6 @@ use super::*; use crate::env; +use crate::sync::mpmc::SendTimeoutError; use crate::thread; use crate::time::Duration; @@ -41,6 +42,13 @@ fn recv_timeout() { assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1)); } +#[test] +fn send_timeout() { + let (tx, _rx) = sync_channel::(1); + assert_eq!(tx.send_timeout(1, Duration::from_millis(1)), Ok(())); + assert_eq!(tx.send_timeout(1, Duration::from_millis(1)), Err(SendTimeoutError::Timeout(1))); +} + #[test] fn smoke_threads() { let (tx, rx) = sync_channel::(0); From 8917e9936282f855a08808ed8874c4117210da6e Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Wed, 11 Jan 2023 21:29:14 -0500 Subject: [PATCH 3/9] rework and document backoff behavior of `sync::mpsc` --- library/std/src/sync/mpmc/array.rs | 14 +++++++------- library/std/src/sync/mpmc/list.rs | 16 ++++++++-------- library/std/src/sync/mpmc/utils.rs | 29 ++++++++++++++--------------- library/std/src/sync/mpmc/zero.rs | 2 +- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs index f71edc6c525a2..c1e3e48b04468 100644 --- a/library/std/src/sync/mpmc/array.rs +++ b/library/std/src/sync/mpmc/array.rs @@ -168,7 +168,7 @@ impl Channel { return true; } Err(_) => { - backoff.spin(); + backoff.spin_light(); tail = self.tail.load(Ordering::Relaxed); } } @@ -182,11 +182,11 @@ impl Channel { return false; } - backoff.spin(); + backoff.spin_light(); tail = self.tail.load(Ordering::Relaxed); } else { // Snooze because we need to wait for the stamp to get updated. - backoff.snooze(); + backoff.spin_heavy(); tail = self.tail.load(Ordering::Relaxed); } } @@ -251,7 +251,7 @@ impl Channel { return true; } Err(_) => { - backoff.spin(); + backoff.spin_light(); head = self.head.load(Ordering::Relaxed); } } @@ -273,11 +273,11 @@ impl Channel { } } - backoff.spin(); + backoff.spin_light(); head = self.head.load(Ordering::Relaxed); } else { // Snooze because we need to wait for the stamp to get updated. - backoff.snooze(); + backoff.spin_heavy(); head = self.head.load(Ordering::Relaxed); } } @@ -330,7 +330,7 @@ impl Channel { if backoff.is_completed() { break; } else { - backoff.spin(); + backoff.spin_light(); } } diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs index 2d5b2fb3b231d..ec6c0726ac790 100644 --- a/library/std/src/sync/mpmc/list.rs +++ b/library/std/src/sync/mpmc/list.rs @@ -46,7 +46,7 @@ impl Slot { fn wait_write(&self) { let backoff = Backoff::new(); while self.state.load(Ordering::Acquire) & WRITE == 0 { - backoff.snooze(); + backoff.spin_heavy(); } } } @@ -82,7 +82,7 @@ impl Block { if !next.is_null() { return next; } - backoff.snooze(); + backoff.spin_heavy(); } } @@ -191,7 +191,7 @@ impl Channel { // If we reached the end of the block, wait until the next one is installed. if offset == BLOCK_CAP { - backoff.snooze(); + backoff.spin_heavy(); tail = self.tail.index.load(Ordering::Acquire); block = self.tail.block.load(Ordering::Acquire); continue; @@ -247,7 +247,7 @@ impl Channel { return true; }, Err(_) => { - backoff.spin(); + backoff.spin_light(); tail = self.tail.index.load(Ordering::Acquire); block = self.tail.block.load(Ordering::Acquire); } @@ -286,7 +286,7 @@ impl Channel { // If we reached the end of the block, wait until the next one is installed. if offset == BLOCK_CAP { - backoff.snooze(); + backoff.spin_heavy(); head = self.head.index.load(Ordering::Acquire); block = self.head.block.load(Ordering::Acquire); continue; @@ -320,7 +320,7 @@ impl Channel { // The block can be null here only if the first message is being sent into the channel. // In that case, just wait until it gets initialized. if block.is_null() { - backoff.snooze(); + backoff.spin_heavy(); head = self.head.index.load(Ordering::Acquire); block = self.head.block.load(Ordering::Acquire); continue; @@ -351,7 +351,7 @@ impl Channel { return true; }, Err(_) => { - backoff.spin(); + backoff.spin_light(); head = self.head.index.load(Ordering::Acquire); block = self.head.block.load(Ordering::Acquire); } @@ -542,7 +542,7 @@ impl Channel { // New updates to tail will be rejected by MARK_BIT and aborted unless it's // at boundary. We need to wait for the updates take affect otherwise there // can be memory leaks. - backoff.snooze(); + backoff.spin_heavy(); tail = self.tail.index.load(Ordering::Acquire); } diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs index a9b365daeec4e..cfe42750d5239 100644 --- a/library/std/src/sync/mpmc/utils.rs +++ b/library/std/src/sync/mpmc/utils.rs @@ -91,9 +91,8 @@ impl DerefMut for CachePadded { } const SPIN_LIMIT: u32 = 6; -const YIELD_LIMIT: u32 = 10; -/// Performs exponential backoff in spin loops. +/// Performs quadratic backoff in spin loops. pub struct Backoff { step: Cell, } @@ -104,25 +103,27 @@ impl Backoff { Backoff { step: Cell::new(0) } } - /// Backs off in a lock-free loop. + /// Backs off using lightweight spinning. /// - /// This method should be used when we need to retry an operation because another thread made - /// progress. + /// This method should be used for: + /// - Retrying an operation because another thread made progress. i.e. on CAS failure. + /// - Waiting for an operation to complete by spinning optimistically for a few iterations + /// before falling back to parking the thread (see `Backoff::is_completed`). #[inline] - pub fn spin(&self) { + pub fn spin_light(&self) { let step = self.step.get().min(SPIN_LIMIT); for _ in 0..step.pow(2) { crate::hint::spin_loop(); } - if self.step.get() <= SPIN_LIMIT { - self.step.set(self.step.get() + 1); - } + self.step.set(self.step.get() + 1); } - /// Backs off in a blocking loop. + /// Backs off using heavyweight spinning. + /// + /// This method should be used in blocking loops where parking the thread is not an option. #[inline] - pub fn snooze(&self) { + pub fn spin_heavy(&self) { if self.step.get() <= SPIN_LIMIT { for _ in 0..self.step.get().pow(2) { crate::hint::spin_loop() @@ -131,12 +132,10 @@ impl Backoff { crate::thread::yield_now(); } - if self.step.get() <= YIELD_LIMIT { - self.step.set(self.step.get() + 1); - } + self.step.set(self.step.get() + 1); } - /// Returns `true` if quadratic backoff has completed and blocking the thread is advised. + /// Returns `true` if quadratic backoff has completed and parking the thread is advised. #[inline] pub fn is_completed(&self) -> bool { self.step.get() > SPIN_LIMIT diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs index fccd6c29a7e46..33f768dcbe902 100644 --- a/library/std/src/sync/mpmc/zero.rs +++ b/library/std/src/sync/mpmc/zero.rs @@ -57,7 +57,7 @@ impl Packet { fn wait_ready(&self) { let backoff = Backoff::new(); while !self.ready.load(Ordering::Acquire) { - backoff.snooze(); + backoff.spin_heavy(); } } } From c82545955ea526c0bb6878ee12fa9959e8de3b89 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 7 Jan 2023 14:02:59 +0800 Subject: [PATCH 4/9] Provide help on closures capturing self causing borrow checker errors --- .../src/diagnostics/conflict_errors.rs | 150 +++++++++++++++++- .../src/diagnostics/mutability_errors.rs | 2 +- ...ssue-105761-suggest-self-for-closure.fixed | 28 ++++ .../issue-105761-suggest-self-for-closure.rs | 28 ++++ ...sue-105761-suggest-self-for-closure.stderr | 49 ++++++ 5 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 tests/ui/suggestions/issue-105761-suggest-self-for-closure.fixed create mode 100644 tests/ui/suggestions/issue-105761-suggest-self-for-closure.rs create mode 100644 tests/ui/suggestions/issue-105761-suggest-self-for-closure.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 6658ee89ad6f1..04bbceadd5a5f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1,4 +1,6 @@ +use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref; use either::Either; +use hir::Closure; use rustc_const_eval::util::CallKind; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; @@ -20,7 +22,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym}; use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; @@ -39,6 +41,8 @@ use super::{ DescribePlaceOpt, RegionName, RegionNameSource, UseSpans, }; +use rustc_hir::def::Res; + #[derive(Debug)] struct MoveSite { /// Index of the "move out" that we found. The `MoveData` can @@ -356,7 +360,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. - })) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id())) + })) = hir.find(self.mir_hir_id()) && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) { let place = &self.move_data.move_paths[mpi].place; @@ -948,7 +952,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } (BorrowKind::Mut { .. }, BorrowKind::Shared) => { first_borrow_desc = "immutable "; - self.cannot_reborrow_already_borrowed( + let mut err = self.cannot_reborrow_already_borrowed( span, &desc_place, &msg_place, @@ -958,7 +962,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "immutable", &msg_borrow, None, - ) + ); + self.suggest_binding_for_closure_capture_self( + &mut err, + issued_borrow.borrowed_place, + &issued_spans, + ); + err } (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => { @@ -1240,6 +1250,138 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + fn suggest_binding_for_closure_capture_self( + &self, + err: &mut Diagnostic, + borrowed_place: Place<'tcx>, + issued_spans: &UseSpans<'tcx>, + ) { + let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return }; + let hir = self.infcx.tcx.hir(); + + // check whether the borrowed place is capturing `self` by mut reference + let local = borrowed_place.local; + let Some(_) = self + .body + .local_decls + .get(local) + .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) else { return }; + + struct ExpressionFinder<'hir> { + capture_span: Span, + closure_change_spans: Vec, + closure_arg_span: Option, + in_closure: bool, + suggest_arg: String, + hir: rustc_middle::hir::map::Map<'hir>, + closure_local_id: Option, + closure_call_changes: Vec<(Span, String)>, + } + impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { + fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { + if e.span.contains(self.capture_span) { + if let hir::ExprKind::Closure(&Closure { + movability: None, + body, + fn_arg_span, + fn_decl: hir::FnDecl{ inputs, .. }, + .. + }) = e.kind && + let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) { + self.suggest_arg = "this: &Self".to_string(); + if inputs.len() > 0 { + self.suggest_arg.push_str(", "); + } + self.in_closure = true; + self.closure_arg_span = fn_arg_span; + self.visit_expr(body); + self.in_closure = false; + } + } + if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e { + if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && + seg.ident.name == kw::SelfLower && self.in_closure { + self.closure_change_spans.push(e.span); + } + } + hir::intravisit::walk_expr(self, e); + } + + fn visit_local(&mut self, local: &'hir hir::Local<'hir>) { + if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat && + let Some(init) = local.init + { + if let hir::Expr { kind: hir::ExprKind::Closure(&Closure { + movability: None, + .. + }), .. } = init && + init.span.contains(self.capture_span) { + self.closure_local_id = Some(*hir_id); + } + } + hir::intravisit::walk_local(self, local); + } + + fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) { + if let hir::StmtKind::Semi(e) = s.kind && + let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind && + let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && + let Res::Local(hir_id) = seg.res && + Some(hir_id) == self.closure_local_id { + let mut arg_str = "self".to_string(); + if args.len() > 0 { + arg_str.push_str(", "); + } + self.closure_call_changes.push((seg.ident.span, arg_str)); + } + hir::intravisit::walk_stmt(self, s); + } + } + + if let Some(hir::Node::ImplItem( + hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. } + )) = hir.find(self.mir_hir_id()) && + let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) { + let mut finder = ExpressionFinder { + capture_span: *capture_kind_span, + closure_change_spans: vec![], + closure_arg_span: None, + in_closure: false, + suggest_arg: String::new(), + closure_local_id: None, + closure_call_changes: vec![], + hir, + }; + finder.visit_expr(expr); + + if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() { + return; + } + + let mut sugg = vec![]; + let sm = self.infcx.tcx.sess.source_map(); + + if let Some(span) = finder.closure_arg_span { + sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg)); + } + for span in finder.closure_change_spans { + sugg.push((span, "this".to_string())); + } + + for (span, suggest) in finder.closure_call_changes { + if let Ok(span) = sm.span_extend_while(span, |c| c != '(') { + sugg.push((sm.next_point(span).shrink_to_hi(), suggest)); + } + } + + err.multipart_suggestion_verbose( + "try explicitly pass `&Self` into the Closure as an argument", + sugg, + Applicability::MachineApplicable, + ); + } + } + /// Returns the description of the root place for a conflicting borrow and the full /// descriptions of the places that caused the conflict. /// diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index b9cfc7e69610e..45b15c2c5bd70 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1094,7 +1094,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } -fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option) -> bool { +pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option) -> bool { debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind()); match local_decl.local_info.as_deref() { diff --git a/tests/ui/suggestions/issue-105761-suggest-self-for-closure.fixed b/tests/ui/suggestions/issue-105761-suggest-self-for-closure.fixed new file mode 100644 index 0000000000000..78e48364bba00 --- /dev/null +++ b/tests/ui/suggestions/issue-105761-suggest-self-for-closure.fixed @@ -0,0 +1,28 @@ +//run-rustfix +#![allow(unused)] + +struct S; +impl S { + fn foo(&mut self) { + let x = |this: &Self, v: i32| { + this.bar(); + this.hel(); + }; + self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable + x(self, 1); + x(self, 3); + } + fn bar(&self) {} + fn hel(&self) {} + fn qux(&mut self) {} + + fn hello(&mut self) { + let y = |this: &Self| { + this.bar(); + }; + self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable + y(self); + } +} + +fn main() {} diff --git a/tests/ui/suggestions/issue-105761-suggest-self-for-closure.rs b/tests/ui/suggestions/issue-105761-suggest-self-for-closure.rs new file mode 100644 index 0000000000000..6d8a9ffc12d39 --- /dev/null +++ b/tests/ui/suggestions/issue-105761-suggest-self-for-closure.rs @@ -0,0 +1,28 @@ +//run-rustfix +#![allow(unused)] + +struct S; +impl S { + fn foo(&mut self) { + let x = |v: i32| { + self.bar(); + self.hel(); + }; + self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable + x(1); + x(3); + } + fn bar(&self) {} + fn hel(&self) {} + fn qux(&mut self) {} + + fn hello(&mut self) { + let y = || { + self.bar(); + }; + self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable + y(); + } +} + +fn main() {} diff --git a/tests/ui/suggestions/issue-105761-suggest-self-for-closure.stderr b/tests/ui/suggestions/issue-105761-suggest-self-for-closure.stderr new file mode 100644 index 0000000000000..bc97d32ebb6e5 --- /dev/null +++ b/tests/ui/suggestions/issue-105761-suggest-self-for-closure.stderr @@ -0,0 +1,49 @@ +error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable + --> $DIR/issue-105761-suggest-self-for-closure.rs:11:9 + | +LL | let x = |v: i32| { + | -------- immutable borrow occurs here +LL | self.bar(); + | ---- first borrow occurs due to use of `self` in closure +... +LL | self.qux(); + | ^^^^^^^^^^ mutable borrow occurs here +LL | x(1); + | - immutable borrow later used here + | +help: try explicitly pass `&Self` into the Closure as an argument + | +LL ~ let x = |this: &Self, v: i32| { +LL ~ this.bar(); +LL ~ this.hel(); +LL | }; +LL | self.qux(); +LL ~ x(self, 1); +LL ~ x(self, 3); + | + +error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable + --> $DIR/issue-105761-suggest-self-for-closure.rs:23:9 + | +LL | let y = || { + | -- immutable borrow occurs here +LL | self.bar(); + | ---- first borrow occurs due to use of `self` in closure +LL | }; +LL | self.qux(); + | ^^^^^^^^^^ mutable borrow occurs here +LL | y(); + | - immutable borrow later used here + | +help: try explicitly pass `&Self` into the Closure as an argument + | +LL ~ let y = |this: &Self| { +LL ~ this.bar(); +LL | }; +LL | self.qux(); +LL ~ y(self); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0502`. From eafbca916632ef8a92570fe3bf2c88ac6fbb3665 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 8 Jan 2023 00:32:40 +0800 Subject: [PATCH 5/9] take care when there is no args in method call --- .../src/diagnostics/conflict_errors.rs | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 04bbceadd5a5f..968c1f49b95c0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1,6 +1,4 @@ -use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref; use either::Either; -use hir::Closure; use rustc_const_eval::util::CallKind; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; @@ -8,6 +6,7 @@ use rustc_errors::{ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem}; use rustc_infer::infer::TyCtxtInferExt; @@ -31,6 +30,7 @@ use crate::borrowck_errors; use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead; use crate::diagnostics::find_all_local_uses; +use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref; use crate::{ borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf, InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind, @@ -41,8 +41,6 @@ use super::{ DescribePlaceOpt, RegionName, RegionNameSource, UseSpans, }; -use rustc_hir::def::Res; - #[derive(Debug)] struct MoveSite { /// Index of the "move out" that we found. The `MoveData` can @@ -1280,7 +1278,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { if e.span.contains(self.capture_span) { - if let hir::ExprKind::Closure(&Closure { + if let hir::ExprKind::Closure(&hir::Closure { movability: None, body, fn_arg_span, @@ -1311,7 +1309,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat && let Some(init) = local.init { - if let hir::Expr { kind: hir::ExprKind::Closure(&Closure { + if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { movability: None, .. }), .. } = init && @@ -1328,11 +1326,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && let Res::Local(hir_id) = seg.res && Some(hir_id) == self.closure_local_id { - let mut arg_str = "self".to_string(); - if args.len() > 0 { - arg_str.push_str(", "); - } - self.closure_call_changes.push((seg.ident.span, arg_str)); + let (span, arg_str) = if args.len() > 0 { + (args[0].span.shrink_to_lo(), "self, ".to_string()) + } else { + let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span); + (span, "(self)".to_string()) + }; + self.closure_call_changes.push((span, arg_str)); } hir::intravisit::walk_stmt(self, s); } @@ -1369,9 +1369,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } for (span, suggest) in finder.closure_call_changes { - if let Ok(span) = sm.span_extend_while(span, |c| c != '(') { - sugg.push((sm.next_point(span).shrink_to_hi(), suggest)); - } + sugg.push((span, suggest)); } err.multipart_suggestion_verbose( From 54571407b2ac4bd29f6509b680a19b49d7fbaa16 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 4 Jan 2023 20:55:37 +0000 Subject: [PATCH 6/9] Bump IMPLIED_BOUNDS_ENTAILMENT to Deny + ReportNow --- compiler/rustc_lint_defs/src/builtin.rs | 4 ++-- ...plied-bounds-compatibility-unnormalized.stderr | 15 +++++++++++++++ .../impl-implied-bounds-compatibility.stderr | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 28317d6cea02a..6cdf50970836a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4033,10 +4033,10 @@ declare_lint! { /// /// This can be used to implement an unsound API if used incorrectly. pub IMPLIED_BOUNDS_ENTAILMENT, - Warn, + Deny, "impl method assumes more implied bounds than its corresponding trait method", @future_incompatible = FutureIncompatibleInfo { reference: "issue #105572 ", - reason: FutureIncompatibilityReason::FutureReleaseError, + reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow, }; } diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr index 0ac31c642eb12..961ff573e5040 100644 --- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr +++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr @@ -14,3 +14,18 @@ LL | #![deny(implied_bounds_entailment)] error: aborting due to previous error +Future incompatibility report: Future breakage diagnostic: +error: impl method assumes more implied bounds than the corresponding trait method + --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:5 + | +LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #105572 +note: the lint level is defined here + --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:1:9 + | +LL | #![deny(implied_bounds_entailment)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr index 0dfa8167a9966..4841f2b179932 100644 --- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr +++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr @@ -14,3 +14,18 @@ LL | #![deny(implied_bounds_entailment)] error: aborting due to previous error +Future incompatibility report: Future breakage diagnostic: +error: impl method assumes more implied bounds than the corresponding trait method + --> $DIR/impl-implied-bounds-compatibility.rs:14:5 + | +LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #105572 +note: the lint level is defined here + --> $DIR/impl-implied-bounds-compatibility.rs:1:9 + | +LL | #![deny(implied_bounds_entailment)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + From eaa7cc84d358a0113c063d445e5d0d7caeeea94c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 12 Jan 2023 20:43:44 +0000 Subject: [PATCH 7/9] Add logic to make IMPLIED_BOUNDS_ENTAILMENT easier to understand --- .../src/check/compare_impl_item.rs | 160 ++++++++++++++++-- ...d-bounds-compatibility-unnormalized.stderr | 8 +- .../impl-implied-bounds-compatibility.stderr | 8 +- 3 files changed, 156 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 7af89934d1420..2cdf75794713f 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2,7 +2,9 @@ use super::potentially_plural_count; use crate::errors::LifetimesOrBoundsMismatchOnTrait; use hir::def_id::{DefId, LocalDefId}; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; +use rustc_errors::{ + pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; @@ -320,15 +322,6 @@ fn compare_method_predicate_entailment<'tcx>( ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())), )); } - let emit_implied_wf_lint = || { - infcx.tcx.struct_span_lint_hir( - rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT, - impl_m_hir_id, - infcx.tcx.def_span(impl_m.def_id), - "impl method assumes more implied bounds than the corresponding trait method", - |lint| lint, - ); - }; // Check that all obligations are satisfied by the implementation's // version. @@ -346,7 +339,7 @@ fn compare_method_predicate_entailment<'tcx>( ) .map(|()| { // If the skip-mode was successful, emit a lint. - emit_implied_wf_lint(); + emit_implied_wf_lint(infcx.tcx, impl_m, impl_m_hir_id, vec![]); }); } CheckImpliedWfMode::Skip => { @@ -382,8 +375,16 @@ fn compare_method_predicate_entailment<'tcx>( CheckImpliedWfMode::Skip, ) .map(|()| { + let bad_args = extract_bad_args_for_implies_lint( + tcx, + &errors, + (trait_m, trait_sig), + // Unnormalized impl sig corresponds to the HIR types written + (impl_m, unnormalized_impl_sig), + impl_m_hir_id, + ); // If the skip-mode was successful, emit a lint. - emit_implied_wf_lint(); + emit_implied_wf_lint(tcx, impl_m, impl_m_hir_id, bad_args); }); } CheckImpliedWfMode::Skip => { @@ -400,6 +401,141 @@ fn compare_method_predicate_entailment<'tcx>( Ok(()) } +fn extract_bad_args_for_implies_lint<'tcx>( + tcx: TyCtxt<'tcx>, + errors: &[infer::RegionResolutionError<'tcx>], + (trait_m, trait_sig): (&ty::AssocItem, ty::FnSig<'tcx>), + (impl_m, impl_sig): (&ty::AssocItem, ty::FnSig<'tcx>), + hir_id: hir::HirId, +) -> Vec<(Span, Option)> { + let mut blame_generics = vec![]; + for error in errors { + // Look for the subregion origin that contains an input/output type + let origin = match error { + infer::RegionResolutionError::ConcreteFailure(o, ..) => o, + infer::RegionResolutionError::GenericBoundFailure(o, ..) => o, + infer::RegionResolutionError::SubSupConflict(_, _, o, ..) => o, + infer::RegionResolutionError::UpperBoundUniverseConflict(.., o, _) => o, + }; + // Extract (possible) input/output types from origin + match origin { + infer::SubregionOrigin::Subtype(trace) => { + if let Some((a, b)) = trace.values.ty() { + blame_generics.extend([a, b]); + } + } + infer::SubregionOrigin::RelateParamBound(_, ty, _) => blame_generics.push(*ty), + infer::SubregionOrigin::ReferenceOutlivesReferent(ty, _) => blame_generics.push(*ty), + _ => {} + } + } + + let fn_decl = tcx.hir().fn_decl_by_hir_id(hir_id).unwrap(); + let opt_ret_ty = match fn_decl.output { + hir::FnRetTy::DefaultReturn(_) => None, + hir::FnRetTy::Return(ty) => Some(ty), + }; + + // Map late-bound regions from trait to impl, so the names are right. + let mapping = std::iter::zip( + tcx.fn_sig(trait_m.def_id).bound_vars(), + tcx.fn_sig(impl_m.def_id).bound_vars(), + ) + .filter_map(|(impl_bv, trait_bv)| { + if let ty::BoundVariableKind::Region(impl_bv) = impl_bv + && let ty::BoundVariableKind::Region(trait_bv) = trait_bv + { + Some((impl_bv, trait_bv)) + } else { + None + } + }) + .collect(); + + // For each arg, see if it was in the "blame" of any of the region errors. + // If so, then try to produce a suggestion to replace the argument type with + // one from the trait. + let mut bad_args = vec![]; + for (idx, (ty, hir_ty)) in + std::iter::zip(impl_sig.inputs_and_output, fn_decl.inputs.iter().chain(opt_ret_ty)) + .enumerate() + { + let expected_ty = trait_sig.inputs_and_output[idx] + .fold_with(&mut RemapLateBound { tcx, mapping: &mapping }); + if blame_generics.iter().any(|blame| ty.contains(*blame)) { + let expected_ty_sugg = expected_ty.to_string(); + bad_args.push(( + hir_ty.span, + // Only suggest something if it actually changed. + (expected_ty_sugg != ty.to_string()).then_some(expected_ty_sugg), + )); + } + } + + bad_args +} + +struct RemapLateBound<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + mapping: &'a FxHashMap, +} + +impl<'tcx> TypeFolder<'tcx> for RemapLateBound<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReFree(fr) = *r { + self.tcx.mk_region(ty::ReFree(ty::FreeRegion { + bound_region: self + .mapping + .get(&fr.bound_region) + .copied() + .unwrap_or(fr.bound_region), + ..fr + })) + } else { + r + } + } +} + +fn emit_implied_wf_lint<'tcx>( + tcx: TyCtxt<'tcx>, + impl_m: &ty::AssocItem, + hir_id: hir::HirId, + bad_args: Vec<(Span, Option)>, +) { + let span: MultiSpan = if bad_args.is_empty() { + tcx.def_span(impl_m.def_id).into() + } else { + bad_args.iter().map(|(span, _)| *span).collect::>().into() + }; + tcx.struct_span_lint_hir( + rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT, + hir_id, + span, + "impl method assumes more implied bounds than the corresponding trait method", + |lint| { + let bad_args: Vec<_> = + bad_args.into_iter().filter_map(|(span, sugg)| Some((span, sugg?))).collect(); + if !bad_args.is_empty() { + lint.multipart_suggestion( + format!( + "replace {} type{} to make the impl signature compatible", + pluralize!("this", bad_args.len()), + pluralize!(bad_args.len()) + ), + bad_args, + Applicability::MaybeIncorrect, + ); + } + lint + }, + ); +} + #[derive(Debug, PartialEq, Eq)] enum CheckImpliedWfMode { /// Checks implied well-formedness of the impl method. If it fails, we will diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr index 961ff573e5040..ebe07027d2fa1 100644 --- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr +++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr @@ -1,8 +1,8 @@ error: impl method assumes more implied bounds than the corresponding trait method - --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:5 + --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31 | LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()` | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #105572 @@ -16,10 +16,10 @@ error: aborting due to previous error Future incompatibility report: Future breakage diagnostic: error: impl method assumes more implied bounds than the corresponding trait method - --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:5 + --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31 | LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()` | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #105572 diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr index 4841f2b179932..43d3e058ffeb3 100644 --- a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr +++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr @@ -1,8 +1,8 @@ error: impl method assumes more implied bounds than the corresponding trait method - --> $DIR/impl-implied-bounds-compatibility.rs:14:5 + --> $DIR/impl-implied-bounds-compatibility.rs:14:35 | LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>` | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #105572 @@ -16,10 +16,10 @@ error: aborting due to previous error Future incompatibility report: Future breakage diagnostic: error: impl method assumes more implied bounds than the corresponding trait method - --> $DIR/impl-implied-bounds-compatibility.rs:14:5 + --> $DIR/impl-implied-bounds-compatibility.rs:14:35 | LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>` | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #105572 From 138a1d26b53cab16066b0faa3846722358a2c09f Mon Sep 17 00:00:00 2001 From: Fawaz Date: Mon, 9 Jan 2023 00:17:58 -0800 Subject: [PATCH 8/9] riscv: Fix ELF header flags The previous version added both `EF_RISCV_FLOAT_ABI_DOUBLE` and `EF_RISCV_RVC` if the "D" extension was enabled on riscv64 targets. riscv32 targets were not accounted for. This patch changes this so that: - Only add `EF_RISCV_RVC` if the "C" extension is enabled - Add `EF_RISCV_FLOAT_ABI_SINGLE` if the "F" extension is enabled and the "D" extension is not - Add these ELF flags for riscv32 as well --- .../rustc_codegen_ssa/src/back/metadata.rs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 51c5c375d5191..5ad2744f61dee 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -165,11 +165,23 @@ pub(crate) fn create_object_file(sess: &Session) -> Option { - // copied from `riscv64-linux-gnu-gcc foo.c -c`, note though - // that the `+d` target feature represents whether the double - // float abi is enabled. - let e_flags = elf::EF_RISCV_RVC | elf::EF_RISCV_FLOAT_ABI_DOUBLE; + Architecture::Riscv32 | Architecture::Riscv64 => { + // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc + let mut e_flags: u32 = 0x0; + let features = &sess.target.options.features; + // Check if compressed is enabled + if features.contains("+c") { + e_flags |= elf::EF_RISCV_RVC; + } + + // Select the appropriate floating-point ABI + if features.contains("+d") { + e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE; + } else if features.contains("+f") { + e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE; + } else { + e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT; + } e_flags } _ => 0, From 549ece703393bf78c7567605862496435cae7202 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 10 Jan 2023 13:57:42 +0100 Subject: [PATCH 9/9] Warn when using panic-strategy abort for proc-macro crates --- compiler/rustc_error_messages/locales/en-US/interface.ftl | 3 +++ compiler/rustc_interface/Cargo.toml | 1 + compiler/rustc_interface/src/errors.rs | 4 ++++ compiler/rustc_interface/src/passes.rs | 8 +++++++- tests/ui/proc-macro/panic-abort.rs | 4 ++++ tests/ui/proc-macro/panic-abort.stderr | 4 ++++ 6 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/ui/proc-macro/panic-abort.rs create mode 100644 tests/ui/proc-macro/panic-abort.stderr diff --git a/compiler/rustc_error_messages/locales/en-US/interface.ftl b/compiler/rustc_error_messages/locales/en-US/interface.ftl index bbcb8fc28cffa..688b044722260 100644 --- a/compiler/rustc_error_messages/locales/en-US/interface.ftl +++ b/compiler/rustc_error_messages/locales/en-US/interface.ftl @@ -41,3 +41,6 @@ interface_rustc_error_unexpected_annotation = interface_failed_writing_file = failed to write file {$path}: {$error}" + +interface_proc_macro_crate_panic_abort = + building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index e67dec31dcee3..f817c5bc1cd73 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -45,6 +45,7 @@ rustc_plugin_impl = { path = "../rustc_plugin_impl" } rustc_privacy = { path = "../rustc_privacy" } rustc_query_impl = { path = "../rustc_query_impl" } rustc_resolve = { path = "../rustc_resolve" } +rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index f5135c78dc831..15d7e977bbe88 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -87,3 +87,7 @@ pub struct FailedWritingFile<'a> { pub path: &'a Path, pub error: io::Error, } + +#[derive(Diagnostic)] +#[diag(interface_proc_macro_crate_panic_abort)] +pub struct ProcMacroCratePanicAbort; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 86d56385bc963..c8085e91dfaeb 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1,7 +1,8 @@ use crate::errors::{ CantEmitMIR, EmojiIdentifier, ErrorWritingDependencies, FerrisIdentifier, GeneratedFileConflictsWithDirectory, InputFileWouldBeOverWritten, MixedBinCrate, - MixedProcMacroCrate, OutDirError, ProcMacroDocWithoutArg, TempsDirError, + MixedProcMacroCrate, OutDirError, ProcMacroCratePanicAbort, ProcMacroDocWithoutArg, + TempsDirError, }; use crate::interface::{Compiler, Result}; use crate::proc_macro_decls; @@ -36,6 +37,7 @@ use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::FileName; +use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use std::any::Any; @@ -380,6 +382,10 @@ pub fn configure_and_expand( } } + if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort { + sess.emit_warning(ProcMacroCratePanicAbort); + } + // For backwards compatibility, we don't try to run proc macro injection // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being // specified. This should only affect users who manually invoke 'rustdoc', as diff --git a/tests/ui/proc-macro/panic-abort.rs b/tests/ui/proc-macro/panic-abort.rs new file mode 100644 index 0000000000000..ad312a875e396 --- /dev/null +++ b/tests/ui/proc-macro/panic-abort.rs @@ -0,0 +1,4 @@ +// error-pattern: building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic +// compile-flags: --crate-type proc-macro -Cpanic=abort +// force-host +// check-pass diff --git a/tests/ui/proc-macro/panic-abort.stderr b/tests/ui/proc-macro/panic-abort.stderr new file mode 100644 index 0000000000000..a6e18614f8f06 --- /dev/null +++ b/tests/ui/proc-macro/panic-abort.stderr @@ -0,0 +1,4 @@ +warning: building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic + +warning: 1 warning emitted +