Auto merge of #85014 - Dylan-DPC:rollup-jzpbkdu, r=Dylan-DPC

Rollup of 11 pull requests

Successful merges:

 - #84409 (Ensure TLS destructors run before thread joins in SGX)
 - #84500 (Add --run flag to compiletest)
 - #84728 (Add test for suggestion to borrow unsized function parameters)
 - #84734 (Add `needs-unwind` and beginning of support for testing `panic=abort` std to compiletest)
 - #84755 (Allow using `core::` in intra-doc links within core itself)
 - #84871 (Disallows `#![feature(no_coverage)]` on stable and beta (using standard crate-level gating))
 - #84872 (Wire up tidy dependency checks for cg_clif)
 - #84896 (Handle incorrect placement of parentheses in trait bounds more gracefully)
 - #84905 (CTFE engine: rename copy → copy_intrinsic, move to intrinsics.rs)
 - #84953 (Remove unneeded call to with_default_session_globals in rustdoc highlight)
 - #84987 (small nits)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2021-05-07 01:16:08 +00:00
commit 1773f14a24
50 changed files with 763 additions and 258 deletions

View File

@ -15,20 +15,12 @@ pub fn expand_deriving_eq(
item: &Annotatable,
push: &mut dyn FnMut(Annotatable),
) {
let span = cx.with_def_site_ctxt(span);
let inline = cx.meta_word(span, sym::inline);
let no_coverage_ident =
rustc_ast::attr::mk_nested_word_item(Ident::new(sym::no_coverage, span));
let no_coverage_feature =
rustc_ast::attr::mk_list_item(Ident::new(sym::feature, span), vec![no_coverage_ident]);
let no_coverage = cx.meta_word(span, sym::no_coverage);
let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span));
let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]);
let attrs = vec![
cx.attribute(inline),
cx.attribute(no_coverage_feature),
cx.attribute(no_coverage),
cx.attribute(doc),
];
let no_coverage = cx.meta_word(span, sym::no_coverage);
let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)];
let trait_def = TraitDef {
span,
attributes: Vec::new(),

View File

@ -273,13 +273,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(List: "address, memory, thread"),
experimental!(no_sanitize)
),
ungated!(
// Not exclusively gated at the crate level (though crate-level is
// supported). The feature can alternatively be enabled on individual
// functions.
no_coverage, AssumedUsed,
template!(Word),
),
gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)),
// FIXME: #14408 assume docs are used since rustdoc looks at them.
ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),

View File

@ -2398,9 +2398,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
self.tcx.associated_item(def_id).ident
),
infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{}`", name),
infer::BoundRegionInCoherence(name) => {
format!(" for lifetime parameter `{}` in coherence check", name)
}
infer::UpvarRegion(ref upvar_id, _) => {
let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
format!(" for capture of `{}` by closure", var_name)

View File

@ -453,8 +453,6 @@ pub enum RegionVariableOrigin {
UpvarRegion(ty::UpvarId, Span),
BoundRegionInCoherence(Symbol),
/// This origin is used for the inference variables that we create
/// during NLL region processing.
Nll(NllRegionVariableOrigin),
@ -1749,7 +1747,6 @@ impl RegionVariableOrigin {
| EarlyBoundRegion(a, ..)
| LateBoundRegion(a, ..)
| UpvarRegion(_, a) => a,
BoundRegionInCoherence(_) => rustc_span::DUMMY_SP,
Nll(..) => bug!("NLL variable used with `span`"),
}
}

View File

@ -323,7 +323,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.write_scalar(result, dest)?;
}
sym::copy => {
self.copy(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
}
sym::offset => {
let ptr = self.read_scalar(&args[0])?.check_init()?;
@ -530,4 +530,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
)?;
Ok(offset_ptr)
}
/// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
pub(crate) fn copy_intrinsic(
&mut self,
src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
nonoverlapping: bool,
) -> InterpResult<'tcx> {
let count = self.read_scalar(&count)?.to_machine_usize(self)?;
let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
let (size, align) = (layout.size, layout.align.abi);
let size = size.checked_mul(count, self).ok_or_else(|| {
err_ub_format!(
"overflow computing total size of `{}`",
if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
)
})?;
// Make sure we check both pointers for an access of the total size and aligment,
// *even if* the total size is 0.
let src =
self.memory.check_ptr_access(self.read_scalar(&src)?.check_init()?, size, align)?;
let dst =
self.memory.check_ptr_access(self.read_scalar(&dst)?.check_init()?, size, align)?;
if let (Some(src), Some(dst)) = (src, dst) {
self.memory.copy(src, dst, size, nonoverlapping)?;
}
Ok(())
}
}

View File

@ -2,7 +2,6 @@
//!
//! The main entry point is the `step` method.
use crate::interpret::OpTy;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_target::abi::LayoutOf;
@ -119,7 +118,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let src = self.eval_operand(src, None)?;
let dst = self.eval_operand(dst, None)?;
let count = self.eval_operand(count, None)?;
self.copy(&src, &dst, &count, /* nonoverlapping */ true)?;
self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true)?;
}
// Statements we do not track.
@ -149,37 +148,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ok(())
}
pub(crate) fn copy(
&mut self,
src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
nonoverlapping: bool,
) -> InterpResult<'tcx> {
let count = self.read_scalar(&count)?.to_machine_usize(self)?;
let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
let (size, align) = (layout.size, layout.align.abi);
let size = size.checked_mul(count, self).ok_or_else(|| {
err_ub_format!(
"overflow computing total size of `{}`",
if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
)
})?;
// Make sure we check both pointers for an access of the total size and aligment,
// *even if* the total size is 0.
let src =
self.memory.check_ptr_access(self.read_scalar(&src)?.check_init()?, size, align)?;
let dst =
self.memory.check_ptr_access(self.read_scalar(&dst)?.check_init()?, size, align)?;
if let (Some(src), Some(dst)) = (src, dst) {
self.memory.copy(src, dst, size, nonoverlapping)?;
}
Ok(())
}
/// Evaluate an assignment statement.
///
/// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue

View File

@ -470,7 +470,7 @@ impl<'a> Parser<'a> {
/// Is a `dyn B0 + ... + Bn` type allowed here?
fn is_explicit_dyn_type(&mut self) -> bool {
self.check_keyword(kw::Dyn)
&& (self.token.uninterpolated_span().rust_2018()
&& (!self.token.uninterpolated_span().rust_2015()
|| self.look_ahead(1, |t| {
t.can_begin_bound() && !can_continue_type_after_non_fn_ident(t)
}))
@ -539,7 +539,21 @@ impl<'a> Parser<'a> {
) -> PResult<'a, GenericBounds> {
let mut bounds = Vec::new();
let mut negative_bounds = Vec::new();
while self.can_begin_bound() {
while self.can_begin_bound() || self.token.is_keyword(kw::Dyn) {
if self.token.is_keyword(kw::Dyn) {
// Account for `&dyn Trait + dyn Other`.
self.struct_span_err(self.token.span, "invalid `dyn` keyword")
.help("`dyn` is only needed at the start of a trait `+`-separated list")
.span_suggestion(
self.token.span,
"remove this keyword",
String::new(),
Applicability::MachineApplicable,
)
.emit();
self.bump();
}
match self.parse_generic_bound()? {
Ok(bound) => bounds.push(bound),
Err(neg_sp) => negative_bounds.push(neg_sp),
@ -721,7 +735,26 @@ impl<'a> Parser<'a> {
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
let path = self.parse_path(PathStyle::Type)?;
if has_parens {
self.expect(&token::CloseDelim(token::Paren))?;
if self.token.is_like_plus() {
// Someone has written something like `&dyn (Trait + Other)`. The correct code
// would be `&(dyn Trait + Other)`, but we don't have access to the appropriate
// span to suggest that. When written as `&dyn Trait + Other`, an appropriate
// suggestion is given.
let bounds = vec![];
self.parse_remaining_bounds(bounds, true)?;
self.expect(&token::CloseDelim(token::Paren))?;
let sp = vec![lo, self.prev_token.span];
let sugg: Vec<_> = sp.iter().map(|sp| (*sp, String::new())).collect();
self.struct_span_err(sp, "incorrect braces around trait bounds")
.multipart_suggestion(
"remove the parentheses",
sugg,
Applicability::MachineApplicable,
)
.emit();
} else {
self.expect(&token::CloseDelim(token::Paren))?;
}
}
let modifier = modifiers.to_trait_bound_modifier();

View File

@ -1044,8 +1044,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
/// Returns `true` if the global caches can be used.
/// Do note that if the type itself is not in the
/// global tcx, the local caches will be used.
fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool {
// If there are any inference variables in the `ParamEnv`, then we
// always use a cache local to this particular scope. Otherwise, we

View File

@ -2661,8 +2661,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
let mut inline_span = None;
let mut link_ordinal_span = None;
let mut no_sanitize_span = None;
let mut no_coverage_feature_enabled = false;
let mut no_coverage_attr = None;
for attr in attrs.iter() {
if tcx.sess.check_name(attr, sym::cold) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
@ -2726,15 +2724,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
} else if tcx.sess.check_name(attr, sym::no_mangle) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
} else if attr.has_name(sym::feature) {
if let Some(list) = attr.meta_item_list() {
if list.iter().any(|nested_meta_item| nested_meta_item.has_name(sym::no_coverage)) {
tcx.sess.mark_attr_used(attr);
no_coverage_feature_enabled = true;
}
}
} else if tcx.sess.check_name(attr, sym::no_coverage) {
no_coverage_attr = Some(attr);
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
} else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
} else if tcx.sess.check_name(attr, sym::used) {
@ -2945,23 +2936,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
}
}
if let Some(no_coverage_attr) = no_coverage_attr {
if tcx.sess.features_untracked().no_coverage || no_coverage_feature_enabled {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE
} else {
let mut err = feature_err(
&tcx.sess.parse_sess,
sym::no_coverage,
no_coverage_attr.span,
"the `#[no_coverage]` attribute is an experimental feature",
);
if tcx.sess.parse_sess.unstable_features.is_nightly_build() {
err.help("or, alternatively, add `#[feature(no_coverage)]` to the function");
}
err.emit();
}
}
codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
if !attr.has_name(sym::inline) {
return ia;

View File

@ -274,8 +274,7 @@ pub trait Eq: PartialEq<Self> {
//
// This should never be implemented by hand.
#[doc(hidden)]
#[cfg_attr(not(bootstrap), feature(no_coverage))]
#[cfg_attr(not(bootstrap), no_coverage)]
#[cfg_attr(not(bootstrap), no_coverage)] // rust-lang/rust#84605
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
fn assert_receiver_is_total_eq(&self) {}
@ -284,7 +283,7 @@ pub trait Eq: PartialEq<Self> {
/// Derive macro generating an impl of the trait `Eq`.
#[rustc_builtin_macro]
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)]
#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match, no_coverage)]
pub macro Eq($item:item) {
/* compiler built-in */
}

View File

@ -723,7 +723,7 @@ extern "rust-intrinsic" {
/// macro, which panics when it is executed, it is *undefined behavior* to
/// reach code marked with this function.
///
/// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`](crate::hint::unreachable_unchecked).
/// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`].
#[rustc_const_unstable(feature = "const_unreachable_unchecked", issue = "53188")]
pub fn unreachable() -> !;
@ -768,13 +768,13 @@ extern "rust-intrinsic" {
/// More specifically, this is the offset in bytes between successive
/// items of the same type, including alignment padding.
///
/// The stabilized version of this intrinsic is [`core::mem::size_of`](crate::mem::size_of).
/// The stabilized version of this intrinsic is [`core::mem::size_of`].
#[rustc_const_stable(feature = "const_size_of", since = "1.40.0")]
pub fn size_of<T>() -> usize;
/// The minimum alignment of a type.
///
/// The stabilized version of this intrinsic is [`core::mem::align_of`](crate::mem::align_of).
/// The stabilized version of this intrinsic is [`core::mem::align_of`].
#[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")]
pub fn min_align_of<T>() -> usize;
/// The preferred alignment of a type.
@ -790,13 +790,13 @@ extern "rust-intrinsic" {
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
/// The required alignment of the referenced value.
///
/// The stabilized version of this intrinsic is [`core::mem::align_of_val`](crate::mem::align_of_val).
/// The stabilized version of this intrinsic is [`core::mem::align_of_val`].
#[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")]
pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
/// Gets a static string slice containing the name of a type.
///
/// The stabilized version of this intrinsic is [`core::any::type_name`](crate::any::type_name).
/// The stabilized version of this intrinsic is [`core::any::type_name`].
#[rustc_const_unstable(feature = "const_type_name", issue = "63084")]
pub fn type_name<T: ?Sized>() -> &'static str;
@ -804,7 +804,7 @@ extern "rust-intrinsic" {
/// function will return the same value for a type regardless of whichever
/// crate it is invoked in.
///
/// The stabilized version of this intrinsic is [`core::any::TypeId::of`](crate::any::TypeId::of).
/// The stabilized version of this intrinsic is [`core::any::TypeId::of`].
#[rustc_const_unstable(feature = "const_type_id", issue = "77125")]
pub fn type_id<T: ?Sized + 'static>() -> u64;
@ -829,7 +829,7 @@ extern "rust-intrinsic" {
/// Gets a reference to a static `Location` indicating where it was called.
///
/// Consider using [`core::panic::Location::caller`](crate::panic::Location::caller) instead.
/// Consider using [`core::panic::Location::caller`] instead.
#[rustc_const_unstable(feature = "const_caller_location", issue = "76156")]
pub fn caller_location() -> &'static crate::panic::Location<'static>;
@ -1158,11 +1158,11 @@ extern "rust-intrinsic" {
/// Performs a volatile load from the `src` pointer.
///
/// The stabilized version of this intrinsic is [`core::ptr::read_volatile`](crate::ptr::read_volatile).
/// The stabilized version of this intrinsic is [`core::ptr::read_volatile`].
pub fn volatile_load<T>(src: *const T) -> T;
/// Performs a volatile store to the `dst` pointer.
///
/// The stabilized version of this intrinsic is [`core::ptr::write_volatile`](crate::ptr::write_volatile).
/// The stabilized version of this intrinsic is [`core::ptr::write_volatile`].
pub fn volatile_store<T>(dst: *mut T, val: T);
/// Performs a volatile load from the `src` pointer
@ -1703,7 +1703,7 @@ extern "rust-intrinsic" {
/// Returns the value of the discriminant for the variant in 'v';
/// if `T` has no discriminant, returns `0`.
///
/// The stabilized version of this intrinsic is [`core::mem::discriminant`](crate::mem::discriminant).
/// The stabilized version of this intrinsic is [`core::mem::discriminant`].
#[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;

View File

@ -166,9 +166,14 @@
#![feature(const_caller_location)]
#![feature(slice_ptr_get)]
#![feature(no_niche)] // rust-lang/rust#68303
#![cfg_attr(not(bootstrap), feature(no_coverage))] // rust-lang/rust#84605
#![feature(int_error_matching)]
#![deny(unsafe_op_in_unsafe_fn)]
// allow using `core::` in intra-doc links
#[allow(unused_extern_crates)]
extern crate self as core;
#[prelude_import]
#[allow(unused)]
use prelude::v1::*;

View File

@ -62,10 +62,12 @@ unsafe extern "C" fn tcs_init(secondary: bool) {
extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn {
// FIXME: how to support TLS in library mode?
let tls = Box::new(tls::Tls::new());
let _tls_guard = unsafe { tls.activate() };
let tls_guard = unsafe { tls.activate() };
if secondary {
super::thread::Thread::entry();
let join_notifier = super::thread::Thread::entry();
drop(tls_guard);
drop(join_notifier);
EntryReturn(0, 0)
} else {

View File

@ -9,26 +9,37 @@ pub struct Thread(task_queue::JoinHandle);
pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
pub use self::task_queue::JoinNotifier;
mod task_queue {
use crate::sync::mpsc;
use super::wait_notify;
use crate::sync::{Mutex, MutexGuard, Once};
pub type JoinHandle = mpsc::Receiver<()>;
pub type JoinHandle = wait_notify::Waiter;
pub struct JoinNotifier(Option<wait_notify::Notifier>);
impl Drop for JoinNotifier {
fn drop(&mut self) {
self.0.take().unwrap().notify();
}
}
pub(super) struct Task {
p: Box<dyn FnOnce()>,
done: mpsc::Sender<()>,
done: JoinNotifier,
}
impl Task {
pub(super) fn new(p: Box<dyn FnOnce()>) -> (Task, JoinHandle) {
let (done, recv) = mpsc::channel();
let (done, recv) = wait_notify::new();
let done = JoinNotifier(Some(done));
(Task { p, done }, recv)
}
pub(super) fn run(self) {
pub(super) fn run(self) -> JoinNotifier {
(self.p)();
let _ = self.done.send(());
self.done
}
}
@ -47,6 +58,48 @@ mod task_queue {
}
}
/// This module provides a synchronization primitive that does not use thread
/// local variables. This is needed for signaling that a thread has finished
/// execution. The signal is sent once all TLS destructors have finished at
/// which point no new thread locals should be created.
pub mod wait_notify {
use super::super::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
use crate::sync::Arc;
pub struct Notifier(Arc<SpinMutex<WaitVariable<bool>>>);
impl Notifier {
/// Notify the waiter. The waiter is either notified right away (if
/// currently blocked in `Waiter::wait()`) or later when it calls the
/// `Waiter::wait()` method.
pub fn notify(self) {
let mut guard = self.0.lock();
*guard.lock_var_mut() = true;
let _ = WaitQueue::notify_one(guard);
}
}
pub struct Waiter(Arc<SpinMutex<WaitVariable<bool>>>);
impl Waiter {
/// Wait for a notification. If `Notifier::notify()` has already been
/// called, this will return immediately, otherwise the current thread
/// is blocked until notified.
pub fn wait(self) {
let guard = self.0.lock();
if *guard.lock_var() {
return;
}
WaitQueue::wait(guard, || {});
}
}
pub fn new() -> (Notifier, Waiter) {
let inner = Arc::new(SpinMutex::new(WaitVariable::new(false)));
(Notifier(inner.clone()), Waiter(inner))
}
}
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
@ -57,7 +110,7 @@ impl Thread {
Ok(Thread(handle))
}
pub(super) fn entry() {
pub(super) fn entry() -> JoinNotifier {
let mut pending_tasks = task_queue::lock();
let task = rtunwrap!(Some, pending_tasks.pop());
drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary
@ -78,7 +131,7 @@ impl Thread {
}
pub fn join(self) {
let _ = self.0.recv();
self.0.wait();
}
}

View File

@ -1,4 +1,5 @@
use crate::cell::{Cell, UnsafeCell};
use crate::sync::atomic::{AtomicU8, Ordering};
use crate::sync::mpsc::{channel, Sender};
use crate::thread::{self, LocalKey};
use crate::thread_local;
@ -207,3 +208,110 @@ fn dtors_in_dtors_in_dtors_const_init() {
});
rx.recv().unwrap();
}
// This test tests that TLS destructors have run before the thread joins. The
// test has no false positives (meaning: if the test fails, there's actually
// an ordering problem). It may have false negatives, where the test passes but
// join is not guaranteed to be after the TLS destructors. However, false
// negatives should be exceedingly rare due to judicious use of
// thread::yield_now and running the test several times.
#[test]
fn join_orders_after_tls_destructors() {
// We emulate a synchronous MPSC rendezvous channel using only atomics and
// thread::yield_now. We can't use std::mpsc as the implementation itself
// may rely on thread locals.
//
// The basic state machine for an SPSC rendezvous channel is:
// FRESH -> THREAD1_WAITING -> MAIN_THREAD_RENDEZVOUS
// where the first transition is done by the “receiving” thread and the 2nd
// transition is done by the “sending” thread.
//
// We add an additional state `THREAD2_LAUNCHED` between `FRESH` and
// `THREAD1_WAITING` to block until all threads are actually running.
//
// A thread that joins on the “receiving” thread completion should never
// observe the channel in the `THREAD1_WAITING` state. If this does occur,
// we switch to the “poison” state `THREAD2_JOINED` and panic all around.
// (This is equivalent to “sending” from an alternate producer thread.)
const FRESH: u8 = 0;
const THREAD2_LAUNCHED: u8 = 1;
const THREAD1_WAITING: u8 = 2;
const MAIN_THREAD_RENDEZVOUS: u8 = 3;
const THREAD2_JOINED: u8 = 4;
static SYNC_STATE: AtomicU8 = AtomicU8::new(FRESH);
for _ in 0..10 {
SYNC_STATE.store(FRESH, Ordering::SeqCst);
let jh = thread::Builder::new()
.name("thread1".into())
.spawn(move || {
struct TlDrop;
impl Drop for TlDrop {
fn drop(&mut self) {
let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::SeqCst);
loop {
match sync_state {
THREAD2_LAUNCHED | THREAD1_WAITING => thread::yield_now(),
MAIN_THREAD_RENDEZVOUS => break,
THREAD2_JOINED => panic!(
"Thread 1 still running after thread 2 joined on thread 1"
),
v => unreachable!("sync state: {}", v),
}
sync_state = SYNC_STATE.load(Ordering::SeqCst);
}
}
}
thread_local! {
static TL_DROP: TlDrop = TlDrop;
}
TL_DROP.with(|_| {});
loop {
match SYNC_STATE.load(Ordering::SeqCst) {
FRESH => thread::yield_now(),
THREAD2_LAUNCHED => break,
v => unreachable!("sync state: {}", v),
}
}
})
.unwrap();
let jh2 = thread::Builder::new()
.name("thread2".into())
.spawn(move || {
assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::SeqCst), FRESH);
jh.join().unwrap();
match SYNC_STATE.swap(THREAD2_JOINED, Ordering::SeqCst) {
MAIN_THREAD_RENDEZVOUS => return,
THREAD2_LAUNCHED | THREAD1_WAITING => {
panic!("Thread 2 running after thread 1 join before main thread rendezvous")
}
v => unreachable!("sync state: {:?}", v),
}
})
.unwrap();
loop {
match SYNC_STATE.compare_exchange_weak(
THREAD1_WAITING,
MAIN_THREAD_RENDEZVOUS,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => break,
Err(FRESH) => thread::yield_now(),
Err(THREAD2_LAUNCHED) => thread::yield_now(),
Err(THREAD2_JOINED) => {
panic!("Main thread rendezvous after thread 2 joined thread 1")
}
v => unreachable!("sync state: {:?}", v),
}
}
jh2.join().unwrap();
}
}

View File

@ -489,6 +489,7 @@ mod dist {
compare_mode: None,
rustfix_coverage: false,
pass: None,
run: None,
};
let build = Build::new(config);
@ -529,6 +530,7 @@ mod dist {
compare_mode: None,
rustfix_coverage: false,
pass: None,
run: None,
};
let build = Build::new(config);
@ -584,6 +586,7 @@ mod dist {
compare_mode: None,
rustfix_coverage: false,
pass: None,
run: None,
};
// Make sure rustfmt binary not being found isn't an error.
config.channel = "beta".to_string();

View File

@ -103,6 +103,7 @@ pub enum Subcommand {
bless: bool,
compare_mode: Option<String>,
pass: Option<String>,
run: Option<String>,
test_args: Vec<String>,
rustc_args: Vec<String>,
fail_fast: bool,
@ -222,8 +223,8 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
VALUE overrides the skip-rebuild option in config.toml.",
"VALUE",
);
opts.optopt("", "rust-profile-generate", "rustc error format", "FORMAT");
opts.optopt("", "rust-profile-use", "rustc error format", "FORMAT");
opts.optopt("", "rust-profile-generate", "generate PGO profile with rustc build", "FORMAT");
opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "FORMAT");
// We can't use getopt to parse the options until we have completed specifying which
// options are valid, but under the current implementation, some options are conditional on
@ -293,6 +294,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
"force {check,build,run}-pass tests to this mode.",
"check | build | run",
);
opts.optopt("", "run", "whether to execute run-* tests", "auto | always | never");
opts.optflag(
"",
"rustfix-coverage",
@ -556,6 +558,7 @@ Arguments:
bless: matches.opt_present("bless"),
compare_mode: matches.opt_str("compare-mode"),
pass: matches.opt_str("pass"),
run: matches.opt_str("run"),
test_args: matches.opt_strs("test-args"),
rustc_args: matches.opt_strs("rustc-args"),
fail_fast: !matches.opt_present("no-fail-fast"),
@ -742,6 +745,13 @@ impl Subcommand {
}
}
pub fn run(&self) -> Option<&str> {
match *self {
Subcommand::Test { ref run, .. } => run.as_ref().map(|s| &s[..]),
_ => None,
}
}
pub fn open(&self) -> bool {
match *self {
Subcommand::Doc { open, .. } => open,

View File

@ -1240,6 +1240,11 @@ note: if you're sure you want to do this, please open an issue as to why. In the
cmd.arg(pass);
}
if let Some(ref run) = builder.config.cmd.run() {
cmd.arg("--run");
cmd.arg(run);
}
if let Some(ref nodejs) = builder.config.nodejs {
cmd.arg("--nodejs").arg(nodejs);
}

View File

@ -13,7 +13,6 @@ use std::iter::Peekable;
use rustc_lexer::{LiteralKind, TokenKind};
use rustc_span::edition::Edition;
use rustc_span::symbol::Symbol;
use rustc_span::with_default_session_globals;
use super::format::Buffer;
@ -238,28 +237,26 @@ impl<'a> Classifier<'a> {
/// possibly giving it an HTML span with a class specifying what flavor of
/// token is used.
fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'a>)) {
with_default_session_globals(|| {
loop {
if self
.tokens
.peek()
.map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident))
.unwrap_or(false)
{
let tokens = self.get_full_ident_path();
for (token, start, end) in tokens {
let text = &self.src[start..end];
self.advance(token, text, sink);
self.byte_pos += text.len() as u32;
}
}
if let Some((token, text)) = self.next() {
loop {
if self
.tokens
.peek()
.map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident))
.unwrap_or(false)
{
let tokens = self.get_full_ident_path();
for (token, start, end) in tokens {
let text = &self.src[start..end];
self.advance(token, text, sink);
} else {
break;
self.byte_pos += text.len() as u32;
}
}
})
if let Some((token, text)) = self.next() {
self.advance(token, text, sink);
} else {
break;
}
}
}
/// Single step of highlighting. This will classify `token`, but maybe also

View File

@ -2,6 +2,7 @@ use super::write_code;
use crate::html::format::Buffer;
use expect_test::expect_file;
use rustc_span::edition::Edition;
use rustc_span::with_default_session_globals;
const STYLE: &str = r#"
<style>
@ -17,21 +18,25 @@ const STYLE: &str = r#"
#[test]
fn test_html_highlighting() {
let src = include_str!("fixtures/sample.rs");
let html = {
let mut out = Buffer::new();
write_code(&mut out, src, Edition::Edition2018);
format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
};
expect_file!["fixtures/sample.html"].assert_eq(&html);
with_default_session_globals(|| {
let src = include_str!("fixtures/sample.rs");
let html = {
let mut out = Buffer::new();
write_code(&mut out, src, Edition::Edition2018);
format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
};
expect_file!["fixtures/sample.html"].assert_eq(&html);
});
}
#[test]
fn test_dos_backline() {
let src = "pub fn foo() {\r\n\
with_default_session_globals(|| {
let src = "pub fn foo() {\r\n\
println!(\"foo\");\r\n\
}\r\n";
let mut html = Buffer::new();
write_code(&mut html, src, Edition::Edition2018);
expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
let mut html = Buffer::new();
write_code(&mut html, src, Edition::Edition2018);
expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
});
}

View File

@ -2,6 +2,7 @@
// == Test [gdb|lldb]-[command|check] are parsed correctly ===
// should-fail
// needs-run-enabled
// compile-flags:-g
// === GDB TESTS ===================================================================================

View File

@ -1,19 +0,0 @@
1| |// Enables `no_coverage` on individual functions
2| |
3| |#[feature(no_coverage)]
4| |#[no_coverage]
5| |fn do_not_add_coverage_1() {
6| | println!("called but not covered");
7| |}
8| |
9| |#[no_coverage]
10| |#[feature(no_coverage)]
11| |fn do_not_add_coverage_2() {
12| | println!("called but not covered");
13| |}
14| |
15| 1|fn main() {
16| 1| do_not_add_coverage_1();
17| 1| do_not_add_coverage_2();
18| 1|}

View File

@ -1,18 +0,0 @@
// Enables `no_coverage` on individual functions
#[feature(no_coverage)]
#[no_coverage]
fn do_not_add_coverage_1() {
println!("called but not covered");
}
#[no_coverage]
#[feature(no_coverage)]
fn do_not_add_coverage_2() {
println!("called but not covered");
}
fn main() {
do_not_add_coverage_1();
do_not_add_coverage_2();
}

View File

@ -1,5 +1,6 @@
// build-pass
// compile-flags: -C panic=unwind
// needs-unwind
// ignore-emscripten no panic_unwind implementation
// ignore-wasm32 no panic_unwind implementation
// ignore-wasm64 no panic_unwind implementation

View File

@ -1,8 +1,13 @@
#![crate_type = "lib"]
#[no_coverage]
#[feature(no_coverage)] // does not have to be enabled before `#[no_coverage]`
fn no_coverage_is_enabled_on_this_function() {}
#[derive(PartialEq, Eq)] // ensure deriving `Eq` does not enable `feature(no_coverage)`
struct Foo {
a: u8,
b: u32,
}
#[no_coverage] //~ ERROR the `#[no_coverage]` attribute is an experimental feature
fn requires_feature_no_coverage() {}
fn requires_feature_no_coverage() -> bool {
let bar = Foo { a: 0, b: 0 };
bar == Foo { a: 0, b: 0 }
}

View File

@ -1,12 +1,11 @@
error[E0658]: the `#[no_coverage]` attribute is an experimental feature
--> $DIR/feature-gate-no_coverage.rs:7:1
--> $DIR/feature-gate-no_coverage.rs:9:1
|
LL | #[no_coverage]
| ^^^^^^^^^^^^^^
|
= note: see issue #84605 <https://github.com/rust-lang/rust/issues/84605> for more information
= help: add `#![feature(no_coverage)]` to the crate attributes to enable
= help: or, alternatively, add `#[feature(no_coverage)]` to the function
error: aborting due to previous error

View File

@ -14,6 +14,7 @@ mod rusti {
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",

View File

@ -1,6 +1,7 @@
// run-pass
// compile-flags: -Zlink-native-libraries=no -Cdefault-linker-libraries=yes
// ignore-windows - this will probably only work on unixish systems
// ignore-fuchsia - missing __libc_start_main for some reason (#84733)
#[link(name = "some-random-non-existent-library", kind = "static")]
extern "C" {}

View File

@ -4,6 +4,7 @@
// run-fail
// revisions: foo bar
// should-fail
// needs-run-enabled
//[foo] error-pattern:bar
//[bar] error-pattern:foo

View File

@ -1,6 +1,7 @@
// aux-build:weak-lang-items.rs
// error-pattern: `#[panic_handler]` function required, but not found
// error-pattern: language item required, but not found: `eh_personality`
// needs-unwind since it affects the error output
// ignore-emscripten compiled with panic=abort, personality not required
#![no_std]

View File

@ -1,5 +1,5 @@
error[E0259]: the name `core` is defined multiple times
--> $DIR/weak-lang-item.rs:8:1
--> $DIR/weak-lang-item.rs:9:1
|
LL | extern crate core;
| ^^^^^^^^^^^^^^^^^^ `core` reimported here

View File

@ -1,5 +1,6 @@
// build-fail
// compile-flags:-C panic=abort -C prefer-dynamic
// needs-unwind
// ignore-musl - no dylibs here
// ignore-emscripten
// ignore-sgx no dynamic lib support

View File

@ -2,6 +2,7 @@
#![allow(unused_variables)]
// compile-flags:-C lto -C panic=unwind
// needs-unwind
// no-prefer-dynamic
// ignore-emscripten no processes
// ignore-sgx no processes

View File

@ -1,4 +1,5 @@
// build-fail
// needs-unwind
// aux-build:panic-runtime-unwind.rs
// aux-build:panic-runtime-abort.rs
// aux-build:wants-panic-runtime-unwind.rs

View File

@ -1,4 +1,5 @@
// build-fail
// needs-unwind
// error-pattern:is incompatible with this crate's strategy of `unwind`
// aux-build:panic-runtime-abort.rs
// aux-build:panic-runtime-lang-items.rs

View File

@ -1,4 +1,5 @@
// build-fail
// needs-unwind
// error-pattern:is incompatible with this crate's strategy of `unwind`
// aux-build:panic-runtime-abort.rs
// aux-build:wants-panic-runtime-abort.rs

View File

@ -0,0 +1,17 @@
// edition:2018
fn foo1(_: &dyn Drop + AsRef<str>) {} //~ ERROR ambiguous `+` in a type
//~^ ERROR only auto traits can be used as additional traits in a trait object
fn foo2(_: &dyn (Drop + AsRef<str>)) {} //~ ERROR incorrect braces around trait bounds
fn foo3(_: &dyn {Drop + AsRef<str>}) {} //~ ERROR expected parameter name, found `{`
//~^ ERROR expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{`
//~| ERROR at least one trait is required for an object type
fn foo4(_: &dyn <Drop + AsRef<str>>) {} //~ ERROR expected identifier, found `<`
fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {} //~ ERROR invalid `dyn` keyword
//~^ ERROR only auto traits can be used as additional traits in a trait object
fn main() {}

View File

@ -0,0 +1,77 @@
error: ambiguous `+` in a type
--> $DIR/trait-object-delimiters.rs:3:13
|
LL | fn foo1(_: &dyn Drop + AsRef<str>) {}
| ^^^^^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(dyn Drop + AsRef<str>)`
error: incorrect braces around trait bounds
--> $DIR/trait-object-delimiters.rs:6:17
|
LL | fn foo2(_: &dyn (Drop + AsRef<str>)) {}
| ^ ^
|
help: remove the parentheses
|
LL | fn foo2(_: &dyn Drop + AsRef<str>) {}
| -- --
error: expected parameter name, found `{`
--> $DIR/trait-object-delimiters.rs:8:17
|
LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
| ^ expected parameter name
error: expected one of `!`, `(`, `)`, `,`, `?`, `for`, lifetime, or path, found `{`
--> $DIR/trait-object-delimiters.rs:8:17
|
LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
| -^ expected one of 8 possible tokens
| |
| help: missing `,`
error: expected identifier, found `<`
--> $DIR/trait-object-delimiters.rs:12:17
|
LL | fn foo4(_: &dyn <Drop + AsRef<str>>) {}
| ^ expected identifier
error: invalid `dyn` keyword
--> $DIR/trait-object-delimiters.rs:14:25
|
LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
| ^^^ help: remove this keyword
|
= help: `dyn` is only needed at the start of a trait `+`-separated list
error[E0225]: only auto traits can be used as additional traits in a trait object
--> $DIR/trait-object-delimiters.rs:3:24
|
LL | fn foo1(_: &dyn Drop + AsRef<str>) {}
| ---- ^^^^^^^^^^ additional non-auto trait
| |
| first non-auto trait
|
= help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Drop + AsRef<str> {}`
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
error[E0224]: at least one trait is required for an object type
--> $DIR/trait-object-delimiters.rs:8:13
|
LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
| ^^^
error[E0225]: only auto traits can be used as additional traits in a trait object
--> $DIR/trait-object-delimiters.rs:14:29
|
LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
| ---- ^^^^^^^^^^ additional non-auto trait
| |
| first non-auto trait
|
= help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Drop + AsRef<str> {}`
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
error: aborting due to 9 previous errors
Some errors have detailed explanations: E0224, E0225.
For more information about an error, try `rustc --explain E0224`.

View File

@ -35,6 +35,7 @@ struct Outer {
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",

View File

@ -0,0 +1,23 @@
// run-rustfix
#![allow(dead_code, unused_variables)]
fn foo1(bar: &str) {}
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP the trait `Sized` is not implemented for `str`
//~| HELP unsized fn params are gated as an unstable feature
//~| HELP function arguments must have a statically known size, borrowed types always have a known size
fn foo2(_bar: &str) {}
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP the trait `Sized` is not implemented for `str`
//~| HELP unsized fn params are gated as an unstable feature
//~| HELP function arguments must have a statically known size, borrowed types always have a known size
fn foo3(_: &str) {}
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP the trait `Sized` is not implemented for `str`
//~| HELP unsized fn params are gated as an unstable feature
//~| HELP function arguments must have a statically known size, borrowed types always have a known size
fn main() {}

View File

@ -0,0 +1,23 @@
// run-rustfix
#![allow(dead_code, unused_variables)]
fn foo1(bar: str) {}
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP the trait `Sized` is not implemented for `str`
//~| HELP unsized fn params are gated as an unstable feature
//~| HELP function arguments must have a statically known size, borrowed types always have a known size
fn foo2(_bar: str) {}
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP the trait `Sized` is not implemented for `str`
//~| HELP unsized fn params are gated as an unstable feature
//~| HELP function arguments must have a statically known size, borrowed types always have a known size
fn foo3(_: str) {}
//~^ ERROR the size for values of type `str` cannot be known at compilation time
//~| HELP the trait `Sized` is not implemented for `str`
//~| HELP unsized fn params are gated as an unstable feature
//~| HELP function arguments must have a statically known size, borrowed types always have a known size
fn main() {}

View File

@ -0,0 +1,42 @@
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/unsized-function-parameter.rs:5:9
|
LL | fn foo1(bar: str) {}
| ^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
LL | fn foo1(bar: &str) {}
| ^
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/unsized-function-parameter.rs:11:9
|
LL | fn foo2(_bar: str) {}
| ^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
LL | fn foo2(_bar: &str) {}
| ^
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/unsized-function-parameter.rs:17:9
|
LL | fn foo3(_: str) {}
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
LL | fn foo3(_: &str) {}
| ^
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -1,6 +1,6 @@
// error-pattern:building tests with panic=abort is not supported
// no-prefer-dynamic
// compile-flags: --test -Cpanic=abort
// compile-flags: --test -Cpanic=abort -Zpanic-abort-tests=no
// run-flags: --test-threads=1
// ignore-wasm no panic or subprocess support

View File

@ -1,4 +1,5 @@
// run-pass
// needs-unwind
// ignore-windows target requires uwtable
// ignore-wasm32-bare no proper panic=unwind support
// compile-flags: -C panic=unwind -C force-unwind-tables=n

View File

@ -27,6 +27,7 @@ pub fn main() {
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",

View File

@ -171,6 +171,12 @@ impl fmt::Display for Debugger {
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PanicStrategy {
Unwind,
Abort,
}
/// Configuration for compiletest
#[derive(Debug, Clone)]
pub struct Config {
@ -249,6 +255,9 @@ pub struct Config {
/// Force the pass mode of a check/build/run-pass test to this mode.
pub force_pass_mode: Option<PassMode>,
/// Explicitly enable or disable running.
pub run: Option<bool>,
/// Write out a parseable log of tests that were run
pub logfile: Option<PathBuf>,
@ -262,6 +271,10 @@ pub struct Config {
/// Flags to pass to the compiler when building for the target
pub target_rustcflags: Option<String>,
/// What panic strategy the target is built with. Unwind supports Abort, but
/// not vice versa.
pub target_panic: PanicStrategy,
/// Target system to be tested
pub target: String,
@ -348,6 +361,15 @@ pub struct Config {
pub npm: Option<String>,
}
impl Config {
pub fn run_enabled(&self) -> bool {
self.run.unwrap_or_else(|| {
// Auto-detect whether to run based on the platform.
!self.target.ends_with("-fuchsia")
})
}
}
#[derive(Debug, Clone)]
pub struct TestPaths {
pub file: PathBuf, // e.g., compile-test/foo/bar/baz.rs

View File

@ -7,7 +7,7 @@ use std::path::{Path, PathBuf};
use tracing::*;
use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PassMode};
use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PanicStrategy, PassMode};
use crate::util;
use crate::{extract_cdb_version, extract_gdb_version};
@ -85,6 +85,10 @@ impl EarlyProps {
props.ignore = true;
}
if !config.run_enabled() && config.parse_name_directive(ln, "needs-run-enabled") {
props.ignore = true;
}
if !rustc_has_sanitizer_support
&& config.parse_name_directive(ln, "needs-sanitizer-support")
{
@ -111,6 +115,12 @@ impl EarlyProps {
props.ignore = true;
}
if config.target_panic == PanicStrategy::Abort
&& config.parse_name_directive(ln, "needs-unwind")
{
props.ignore = true;
}
if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) {
props.ignore = true;
}

View File

@ -5,7 +5,9 @@
extern crate test;
use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
use crate::common::{
expected_output_path, output_base_dir, output_relative_path, PanicStrategy, UI_EXTENSIONS,
};
use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, Pretty, TestPaths};
use crate::util::logv;
use getopts::Options;
@ -87,6 +89,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
"force {check,build,run}-pass tests to this mode.",
"check | build | run",
)
.optopt("", "run", "whether to execute run-* tests", "auto | always | never")
.optflag("", "ignored", "run tests marked as ignored")
.optflag("", "exact", "filters match exactly")
.optopt(
@ -96,8 +99,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
(eg. emulator, valgrind)",
"PROGRAM",
)
.optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
.optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
.optmulti("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
.optmulti("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
.optopt("", "target-panic", "what panic strategy the target supports", "unwind | abort")
.optflag("", "verbose", "run tests verbosely, showing all output")
.optflag(
"",
@ -234,10 +238,21 @@ pub fn parse_config(args: Vec<String>) -> Config {
mode.parse::<PassMode>()
.unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
}),
run: matches.opt_str("run").and_then(|mode| match mode.as_str() {
"auto" => None,
"always" => Some(true),
"never" => Some(false),
_ => panic!("unknown `--run` option `{}` given", mode),
}),
logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
runtool: matches.opt_str("runtool"),
host_rustcflags: matches.opt_str("host-rustcflags"),
target_rustcflags: matches.opt_str("target-rustcflags"),
host_rustcflags: Some(matches.opt_strs("host-rustcflags").join(" ")),
target_rustcflags: Some(matches.opt_strs("target-rustcflags").join(" ")),
target_panic: match matches.opt_str("target-panic").as_deref() {
Some("unwind") | None => PanicStrategy::Unwind,
Some("abort") => PanicStrategy::Abort,
_ => panic!("unknown `--target-panic` option `{}` given", mode),
},
target,
host: opt_str2(matches.opt_str("host")),
cdb,

View File

@ -259,6 +259,7 @@ pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
pub fn compute_stamp_hash(config: &Config) -> String {
let mut hash = DefaultHasher::new();
config.stage_id.hash(&mut hash);
config.run.hash(&mut hash);
match config.debugger {
Some(Debugger::Cdb) => {
@ -317,6 +318,7 @@ enum TestOutput {
enum WillExecute {
Yes,
No,
Disabled,
}
/// Should `--emit metadata` be used?
@ -357,14 +359,17 @@ impl<'test> TestCx<'test> {
}
fn should_run(&self, pm: Option<PassMode>) -> WillExecute {
match self.config.mode {
Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => {
WillExecute::Yes
}
MirOpt if pm == Some(PassMode::Run) => WillExecute::Yes,
Ui | MirOpt => WillExecute::No,
let test_should_run = match self.config.mode {
Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => true,
MirOpt if pm == Some(PassMode::Run) => true,
Ui | MirOpt => false,
mode => panic!("unimplemented for mode {:?}", mode),
}
};
if test_should_run { self.run_if_enabled() } else { WillExecute::No }
}
fn run_if_enabled(&self) -> WillExecute {
if self.config.run_enabled() { WillExecute::Yes } else { WillExecute::Disabled }
}
fn should_run_successfully(&self, pm: Option<PassMode>) -> bool {
@ -439,12 +444,17 @@ impl<'test> TestCx<'test> {
fn run_rfail_test(&self) {
let pm = self.pass_mode();
let proc_res = self.compile_test(WillExecute::Yes, self.should_emit_metadata(pm));
let should_run = self.run_if_enabled();
let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm));
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
}
if let WillExecute::Disabled = should_run {
return;
}
let proc_res = self.exec_compiled_test();
// The value our Makefile configures valgrind to return on failure
@ -483,12 +493,17 @@ impl<'test> TestCx<'test> {
fn run_rpass_test(&self) {
let emit_metadata = self.should_emit_metadata(self.pass_mode());
let proc_res = self.compile_test(WillExecute::Yes, emit_metadata);
let should_run = self.run_if_enabled();
let proc_res = self.compile_test(should_run, emit_metadata);
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
}
if let WillExecute::Disabled = should_run {
return;
}
// FIXME(#41968): Move this check to tidy?
let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
assert!(
@ -510,12 +525,17 @@ impl<'test> TestCx<'test> {
return self.run_rpass_test();
}
let mut proc_res = self.compile_test(WillExecute::Yes, EmitMetadata::No);
let should_run = self.run_if_enabled();
let mut proc_res = self.compile_test(should_run, EmitMetadata::No);
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
}
if let WillExecute::Disabled = should_run {
return;
}
let mut new_config = self.config.clone();
new_config.runtool = new_config.valgrind_path.clone();
let new_cx = TestCx { config: &new_config, ..*self };
@ -732,10 +752,14 @@ impl<'test> TestCx<'test> {
fn run_debuginfo_cdb_test_no_opt(&self) {
// compile test file (it should have 'compile-flags:-g' in the header)
let compile_result = self.compile_test(WillExecute::Yes, EmitMetadata::No);
let should_run = self.run_if_enabled();
let compile_result = self.compile_test(should_run, EmitMetadata::No);
if !compile_result.status.success() {
self.fatal_proc_rec("compilation failed!", &compile_result);
}
if let WillExecute::Disabled = should_run {
return;
}
let exe_file = self.make_exe_name();
@ -826,10 +850,14 @@ impl<'test> TestCx<'test> {
let mut cmds = commands.join("\n");
// compile test file (it should have 'compile-flags:-g' in the header)
let compiler_run_result = self.compile_test(WillExecute::Yes, EmitMetadata::No);
let should_run = self.run_if_enabled();
let compiler_run_result = self.compile_test(should_run, EmitMetadata::No);
if !compiler_run_result.status.success() {
self.fatal_proc_rec("compilation failed!", &compiler_run_result);
}
if let WillExecute::Disabled = should_run {
return;
}
let exe_file = self.make_exe_name();
@ -1044,10 +1072,14 @@ impl<'test> TestCx<'test> {
fn run_debuginfo_lldb_test_no_opt(&self) {
// compile test file (it should have 'compile-flags:-g' in the header)
let compile_result = self.compile_test(WillExecute::Yes, EmitMetadata::No);
let should_run = self.run_if_enabled();
let compile_result = self.compile_test(should_run, EmitMetadata::No);
if !compile_result.status.success() {
self.fatal_proc_rec("compilation failed!", &compile_result);
}
if let WillExecute::Disabled = should_run {
return;
}
let exe_file = self.make_exe_name();
@ -1531,7 +1563,9 @@ impl<'test> TestCx<'test> {
// Only use `make_exe_name` when the test ends up being executed.
let output_file = match will_execute {
WillExecute::Yes => TargetLocation::ThisFile(self.make_exe_name()),
WillExecute::No => TargetLocation::ThisDirectory(self.output_base_dir()),
WillExecute::No | WillExecute::Disabled => {
TargetLocation::ThisDirectory(self.output_base_dir())
}
};
let allow_unused = match self.config.mode {

View File

@ -44,12 +44,29 @@ const EXCEPTIONS: &[(&str, &str)] = &[
("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target
];
const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"),
("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"),
("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"),
("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"),
("cranelift-entity", "Apache-2.0 WITH LLVM-exception"),
("cranelift-frontend", "Apache-2.0 WITH LLVM-exception"),
("cranelift-jit", "Apache-2.0 WITH LLVM-exception"),
("cranelift-module", "Apache-2.0 WITH LLVM-exception"),
("cranelift-native", "Apache-2.0 WITH LLVM-exception"),
("cranelift-object", "Apache-2.0 WITH LLVM-exception"),
("libloading", "ISC"),
("mach", "BSD-2-Clause"),
("regalloc", "Apache-2.0 WITH LLVM-exception"),
("target-lexicon", "Apache-2.0 WITH LLVM-exception"),
];
/// These are the root crates that are part of the runtime. The licenses for
/// these and all their dependencies *must not* be in the exception list.
const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"];
/// Crates whose dependencies must be explicitly permitted.
const RESTRICTED_DEPENDENCY_CRATES: &[&str] = &["rustc_middle", "rustc_codegen_llvm"];
const RESTRICTED_DEPENDENCY_CRATES: &[&str] = &["rustc_driver", "rustc_codegen_llvm"];
/// Crates rustc is allowed to depend on. Avoid adding to the list if possible.
///
@ -72,7 +89,10 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"cc",
"cfg-if",
"chalk-derive",
"chalk-engine",
"chalk-ir",
"chalk-solve",
"chrono",
"cmake",
"compiler_builtins",
"cpuid-bool",
@ -92,6 +112,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"expect-test",
"fake-simd",
"filetime",
"fixedbitset",
"flate2",
"fortanix-sgx-abi",
"fuchsia-zircon",
@ -107,6 +128,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"indexmap",
"instant",
"itertools",
"itoa",
"jobserver",
"kernel32-sys",
"lazy_static",
@ -114,6 +136,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"libz-sys",
"lock_api",
"log",
"matchers",
"maybe-uninit",
"md-5",
"measureme",
@ -123,6 +146,8 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"memoffset",
"miniz_oxide",
"num_cpus",
"num-integer",
"num-traits",
"object",
"once_cell",
"opaque-debug",
@ -130,6 +155,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"parking_lot_core",
"pathdiff",
"perf-event-open-sys",
"petgraph",
"pin-project-lite",
"pkg-config",
"polonius-engine",
@ -147,22 +173,28 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"rand_xorshift",
"redox_syscall",
"regex",
"regex-automata",
"regex-syntax",
"remove_dir_all",
"rls-data",
"rls-span",
"rustc-demangle",
"rustc-hash",
"rustc-rayon",
"rustc-rayon-core",
"rustc_version",
"ryu",
"scoped-tls",
"scopeguard",
"semver",
"semver-parser",
"serde",
"serde_derive",
"serde_json",
"sha-1",
"sha2",
"smallvec",
"sharded-slab",
"snap",
"stable_deref_trait",
"stacker",
@ -172,9 +204,15 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"termcolor",
"termize",
"thread_local",
"time",
"tinyvec",
"tracing",
"tracing-attributes",
"tracing-core",
"tracing-log",
"tracing-serde",
"tracing-subscriber",
"tracing-tree",
"typenum",
"unicode-normalization",
"unicode-script",
@ -193,6 +231,59 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
"yansi-term",
];
const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
"anyhow",
"ar",
"autocfg",
"bitflags",
"byteorder",
"cfg-if",
"cranelift-bforest",
"cranelift-codegen",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
"cranelift-entity",
"cranelift-frontend",
"cranelift-jit",
"cranelift-module",
"cranelift-native",
"cranelift-object",
"crc32fast",
"errno",
"errno-dragonfly",
"gcc",
"gimli",
"hashbrown",
"indexmap",
"libc",
"libloading",
"log",
"mach",
"object",
"proc-macro2",
"quote",
"regalloc",
"region",
"rustc-hash",
"smallvec",
"syn",
"target-lexicon",
"thiserror",
"thiserror-impl",
"unicode-xid",
"winapi",
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
];
const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
// These two crates take quite a long time to build, so don't allow two versions of them
// to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
// under control.
"cargo",
"rustc-ap-rustc_ast",
];
/// Dependency checks.
///
/// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
@ -203,17 +294,39 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
.manifest_path(root.join("Cargo.toml"))
.features(cargo_metadata::CargoOpt::AllFeatures);
let metadata = t!(cmd.exec());
check_exceptions(&metadata, bad);
check_dependencies(&metadata, bad);
check_crate_duplicate(&metadata, bad);
let runtime_ids = compute_runtime_crates(&metadata);
check_exceptions(&metadata, EXCEPTIONS, runtime_ids, bad);
check_dependencies(&metadata, PERMITTED_DEPENDENCIES, RESTRICTED_DEPENDENCY_CRATES, bad);
check_crate_duplicate(&metadata, FORBIDDEN_TO_HAVE_DUPLICATES, bad);
// Check rustc_codegen_cranelift independently as it has it's own workspace.
let mut cmd = cargo_metadata::MetadataCommand::new();
cmd.cargo_path(cargo)
.manifest_path(root.join("compiler/rustc_codegen_cranelift/Cargo.toml"))
.features(cargo_metadata::CargoOpt::AllFeatures);
let metadata = t!(cmd.exec());
let runtime_ids = HashSet::new();
check_exceptions(&metadata, EXCEPTIONS_CRANELIFT, runtime_ids, bad);
check_dependencies(
&metadata,
PERMITTED_CRANELIFT_DEPENDENCIES,
&["rustc_codegen_cranelift"],
bad,
);
check_crate_duplicate(&metadata, &[], bad);
}
/// Check that all licenses are in the valid list in `LICENSES`.
///
/// Packages listed in `EXCEPTIONS` are allowed for tools.
fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
fn check_exceptions(
metadata: &Metadata,
exceptions: &[(&str, &str)],
runtime_ids: HashSet<&PackageId>,
bad: &mut bool,
) {
// Validate the EXCEPTIONS list hasn't changed.
for (name, license) in EXCEPTIONS {
for (name, license) in exceptions {
// Check that the package actually exists.
if !metadata.packages.iter().any(|p| p.name == *name) {
tidy_error!(
@ -225,13 +338,6 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
}
// Check that the license hasn't changed.
for pkg in metadata.packages.iter().filter(|p| p.name == *name) {
if pkg.name == "fuchsia-cprng" {
// This package doesn't declare a license expression. Manual
// inspection of the license file is necessary, which appears
// to be BSD-3-Clause.
assert!(pkg.license.is_none());
continue;
}
match &pkg.license {
None => {
tidy_error!(
@ -242,14 +348,6 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
}
Some(pkg_license) => {
if pkg_license.as_str() != *license {
if *name == "crossbeam-queue"
&& *license == "MIT/Apache-2.0 AND BSD-2-Clause"
{
// We have two versions of crossbeam-queue and both
// are fine.
continue;
}
println!("dependency exception `{}` license has changed", name);
println!(" previously `{}` now `{}`", license, pkg_license);
println!(" update EXCEPTIONS for the new license");
@ -260,8 +358,7 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
}
}
let exception_names: Vec<_> = EXCEPTIONS.iter().map(|(name, _license)| *name).collect();
let runtime_ids = compute_runtime_crates(metadata);
let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect();
// Check if any package does not have a valid license.
for pkg in &metadata.packages {
@ -296,9 +393,14 @@ fn check_exceptions(metadata: &Metadata, bad: &mut bool) {
/// `true` if a check failed.
///
/// Specifically, this checks that the dependencies are on the `PERMITTED_DEPENDENCIES`.
fn check_dependencies(metadata: &Metadata, bad: &mut bool) {
fn check_dependencies(
metadata: &Metadata,
permitted_dependencies: &[&'static str],
restricted_dependency_crates: &[&'static str],
bad: &mut bool,
) {
// Check that the PERMITTED_DEPENDENCIES does not have unused entries.
for name in PERMITTED_DEPENDENCIES {
for name in permitted_dependencies {
if !metadata.packages.iter().any(|p| p.name == *name) {
tidy_error!(
bad,
@ -309,12 +411,12 @@ fn check_dependencies(metadata: &Metadata, bad: &mut bool) {
}
}
// Get the list in a convenient form.
let permitted_dependencies: HashSet<_> = PERMITTED_DEPENDENCIES.iter().cloned().collect();
let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
// Check dependencies.
let mut visited = BTreeSet::new();
let mut unapproved = BTreeSet::new();
for &krate in RESTRICTED_DEPENDENCY_CRATES.iter() {
for &krate in restricted_dependency_crates.iter() {
let pkg = pkg_from_name(metadata, krate);
let mut bad =
check_crate_dependencies(&permitted_dependencies, metadata, &mut visited, pkg);
@ -367,16 +469,12 @@ fn check_crate_dependencies<'a>(
}
/// Prevents multiple versions of some expensive crates.
fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) {
const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
// These two crates take quite a long time to build, so don't allow two versions of them
// to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
// under control.
"cargo",
"rustc-ap-rustc_ast",
];
for &name in FORBIDDEN_TO_HAVE_DUPLICATES {
fn check_crate_duplicate(
metadata: &Metadata,
forbidden_to_have_duplicates: &[&str],
bad: &mut bool,
) {
for &name in forbidden_to_have_duplicates {
let matches: Vec<_> = metadata.packages.iter().filter(|pkg| pkg.name == name).collect();
match matches.len() {
0 => {
@ -456,16 +554,7 @@ fn normal_deps_of_r<'a>(
.iter()
.find(|n| &n.id == pkg_id)
.unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id));
// Don't care about dev-dependencies.
// Build dependencies *shouldn't* matter unless they do some kind of
// codegen. For now we'll assume they don't.
let deps = node.deps.iter().filter(|node_dep| {
node_dep
.dep_kinds
.iter()
.any(|kind_info| kind_info.kind == cargo_metadata::DependencyKind::Normal)
});
for dep in deps {
for dep in &node.deps {
normal_deps_of_r(resolve, &dep.pkg, result);
}
}