mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-06 15:07:36 +00:00
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:
commit
1773f14a24
@ -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(),
|
||||
|
@ -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")),
|
||||
|
@ -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)
|
||||
|
@ -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`"),
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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::*;
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
});
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
// == Test [gdb|lldb]-[command|check] are parsed correctly ===
|
||||
// should-fail
|
||||
// needs-run-enabled
|
||||
// compile-flags:-g
|
||||
|
||||
// === GDB TESTS ===================================================================================
|
||||
|
@ -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|}
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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" {}
|
||||
|
@ -4,6 +4,7 @@
|
||||
// run-fail
|
||||
// revisions: foo bar
|
||||
// should-fail
|
||||
// needs-run-enabled
|
||||
//[foo] error-pattern:bar
|
||||
//[bar] error-pattern:foo
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
17
src/test/ui/parser/trait-object-delimiters.rs
Normal file
17
src/test/ui/parser/trait-object-delimiters.rs
Normal 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() {}
|
77
src/test/ui/parser/trait-object-delimiters.stderr
Normal file
77
src/test/ui/parser/trait-object-delimiters.stderr
Normal 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`.
|
@ -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",
|
||||
|
23
src/test/ui/suggestions/unsized-function-parameter.fixed
Normal file
23
src/test/ui/suggestions/unsized-function-parameter.fixed
Normal 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() {}
|
23
src/test/ui/suggestions/unsized-function-parameter.rs
Normal file
23
src/test/ui/suggestions/unsized-function-parameter.rs
Normal 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() {}
|
42
src/test/ui/suggestions/unsized-function-parameter.stderr
Normal file
42
src/test/ui/suggestions/unsized-function-parameter.stderr
Normal 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`.
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user