diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 083aa6cf697..2ec509b9118 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -41,6 +41,8 @@ //! [^2] `MTLockRef` is a typedef. pub use crate::marker::*; +use parking_lot::Mutex; +use std::any::Any; use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; use std::ops::{Deref, DerefMut}; @@ -103,6 +105,37 @@ mod mode { pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; +/// A guard used to hold panics that occur during a parallel section to later by unwound. +/// This is used for the parallel compiler to prevent fatal errors from non-deterministically +/// hiding errors by ensuring that everything in the section has completed executing before +/// continuing with unwinding. It's also used for the non-parallel code to ensure error message +/// output match the parallel compiler for testing purposes. +pub struct ParallelGuard { + panic: Mutex>>, +} + +impl ParallelGuard { + pub fn run(&self, f: impl FnOnce() -> R) -> Option { + catch_unwind(AssertUnwindSafe(f)) + .map_err(|err| { + *self.panic.lock() = Some(err); + }) + .ok() + } +} + +/// This gives access to a fresh parallel guard in the closure and will unwind any panics +/// caught in it after the closure returns. +#[inline] +pub fn parallel_guard(f: impl FnOnce(&ParallelGuard) -> R) -> R { + let guard = ParallelGuard { panic: Mutex::new(None) }; + let ret = f(&guard); + if let Some(panic) = guard.panic.into_inner() { + resume_unwind(panic); + } + ret +} + cfg_if! { if #[cfg(not(parallel_compiler))] { use std::ops::Add; @@ -198,67 +231,38 @@ cfg_if! { where A: FnOnce() -> RA, B: FnOnce() -> RB { - (oper_a(), oper_b()) + let (a, b) = parallel_guard(|guard| { + let a = guard.run(oper_a); + let b = guard.run(oper_b); + (a, b) + }); + (a.unwrap(), b.unwrap()) } #[macro_export] macro_rules! parallel { - ($($blocks:block),*) => { - // We catch panics here ensuring that all the blocks execute. - // This makes behavior consistent with the parallel compiler. - let mut panic = None; - $( - if let Err(p) = ::std::panic::catch_unwind( - ::std::panic::AssertUnwindSafe(|| $blocks) - ) { - if panic.is_none() { - panic = Some(p); - } - } - )* - if let Some(panic) = panic { - ::std::panic::resume_unwind(panic); - } - } + ($($blocks:block),*) => {{ + $crate::sync::parallel_guard(|guard| { + $(guard.run(|| $blocks);)* + }); + }} } pub fn par_for_each_in(t: T, mut for_each: impl FnMut(T::Item) + Sync + Send) { - // We catch panics here ensuring that all the loop iterations execute. - // This makes behavior consistent with the parallel compiler. - let mut panic = None; - t.into_iter().for_each(|i| { - if let Err(p) = catch_unwind(AssertUnwindSafe(|| for_each(i))) { - if panic.is_none() { - panic = Some(p); - } - } - }); - if let Some(panic) = panic { - resume_unwind(panic); - } + parallel_guard(|guard| { + t.into_iter().for_each(|i| { + guard.run(|| for_each(i)); + }); + }) } pub fn par_map>( t: T, mut map: impl FnMut(<::IntoIter as Iterator>::Item) -> R, ) -> C { - // We catch panics here ensuring that all the loop iterations execute. - let mut panic = None; - let r = t.into_iter().filter_map(|i| { - match catch_unwind(AssertUnwindSafe(|| map(i))) { - Ok(r) => Some(r), - Err(p) => { - if panic.is_none() { - panic = Some(p); - } - None - } - } - }).collect(); - if let Some(panic) = panic { - resume_unwind(panic); - } - r + parallel_guard(|guard| { + t.into_iter().filter_map(|i| guard.run(|| map(i))).collect() + }) } pub use std::rc::Rc as Lrc; @@ -313,8 +317,6 @@ cfg_if! { } } } else { - use parking_lot::Mutex; - pub use std::marker::Send as Send; pub use std::marker::Sync as Sync; @@ -380,7 +382,12 @@ cfg_if! { let (a, b) = rayon::join(move || FromDyn::from(oper_a.into_inner()()), move || FromDyn::from(oper_b.into_inner()())); (a.into_inner(), b.into_inner()) } else { - (oper_a(), oper_b()) + let (a, b) = parallel_guard(|guard| { + let a = guard.run(oper_a); + let b = guard.run(oper_b); + (a, b) + }); + (a.unwrap(), b.unwrap()) } } @@ -415,28 +422,10 @@ cfg_if! { // of a single threaded rustc. parallel!(impl $fblock [] [$($blocks),*]); } else { - // We catch panics here ensuring that all the blocks execute. - // This makes behavior consistent with the parallel compiler. - let mut panic = None; - if let Err(p) = ::std::panic::catch_unwind( - ::std::panic::AssertUnwindSafe(|| $fblock) - ) { - if panic.is_none() { - panic = Some(p); - } - } - $( - if let Err(p) = ::std::panic::catch_unwind( - ::std::panic::AssertUnwindSafe(|| $blocks) - ) { - if panic.is_none() { - panic = Some(p); - } - } - )* - if let Some(panic) = panic { - ::std::panic::resume_unwind(panic); - } + $crate::sync::parallel_guard(|guard| { + guard.run(|| $fblock); + $(guard.run(|| $blocks);)* + }); } }; } @@ -447,34 +436,18 @@ cfg_if! { t: T, for_each: impl Fn(I) + DynSync + DynSend ) { - if mode::is_dyn_thread_safe() { - let for_each = FromDyn::from(for_each); - let panic: Mutex> = Mutex::new(None); - t.into_par_iter().for_each(|i| if let Err(p) = catch_unwind(AssertUnwindSafe(|| for_each(i))) { - let mut l = panic.lock(); - if l.is_none() { - *l = Some(p) - } - }); - - if let Some(panic) = panic.into_inner() { - resume_unwind(panic); + parallel_guard(|guard| { + if mode::is_dyn_thread_safe() { + let for_each = FromDyn::from(for_each); + t.into_par_iter().for_each(|i| { + guard.run(|| for_each(i)); + }); + } else { + t.into_iter().for_each(|i| { + guard.run(|| for_each(i)); + }); } - } else { - // We catch panics here ensuring that all the loop iterations execute. - // This makes behavior consistent with the parallel compiler. - let mut panic = None; - t.into_iter().for_each(|i| { - if let Err(p) = catch_unwind(AssertUnwindSafe(|| for_each(i))) { - if panic.is_none() { - panic = Some(p); - } - } - }); - if let Some(panic) = panic { - resume_unwind(panic); - } - } + }); } pub fn par_map< @@ -486,46 +459,14 @@ cfg_if! { t: T, map: impl Fn(I) -> R + DynSync + DynSend ) -> C { - if mode::is_dyn_thread_safe() { - let panic: Mutex> = Mutex::new(None); - let map = FromDyn::from(map); - // We catch panics here ensuring that all the loop iterations execute. - let r = t.into_par_iter().filter_map(|i| { - match catch_unwind(AssertUnwindSafe(|| map(i))) { - Ok(r) => Some(r), - Err(p) => { - let mut l = panic.lock(); - if l.is_none() { - *l = Some(p); - } - None - }, - } - }).collect(); - - if let Some(panic) = panic.into_inner() { - resume_unwind(panic); + parallel_guard(|guard| { + if mode::is_dyn_thread_safe() { + let map = FromDyn::from(map); + t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect() + } else { + t.into_iter().filter_map(|i| guard.run(|| map(i))).collect() } - r - } else { - // We catch panics here ensuring that all the loop iterations execute. - let mut panic = None; - let r = t.into_iter().filter_map(|i| { - match catch_unwind(AssertUnwindSafe(|| map(i))) { - Ok(r) => Some(r), - Err(p) => { - if panic.is_none() { - panic = Some(p); - } - None - } - } - }).collect(); - if let Some(panic) = panic { - resume_unwind(panic); - } - r - } + }) } /// This makes locks panic if they are already held. diff --git a/tests/ui/const-generics/late-bound-vars/in_closure.rs b/tests/ui/const-generics/late-bound-vars/in_closure.rs index 4fdf603b05f..6549cad629b 100644 --- a/tests/ui/const-generics/late-bound-vars/in_closure.rs +++ b/tests/ui/const-generics/late-bound-vars/in_closure.rs @@ -1,4 +1,4 @@ -// failure-status: 101 +// failure-status: 1 // known-bug: unknown // error-pattern:internal compiler error // normalize-stderr-test "internal compiler error.*" -> "" @@ -22,7 +22,10 @@ #![feature(generic_const_exprs)] #![allow(incomplete_features)] -const fn inner<'a>() -> usize where &'a (): Sized { +const fn inner<'a>() -> usize +where + &'a (): Sized, +{ 3 }