mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 00:34:06 +00:00
Auto merge of #64510 - Centril:rollup-m03zsq8, r=Centril
Rollup of 10 pull requests Successful merges: - #63955 (Make sure interned constants are immutable) - #64028 (Stabilize `Vec::new` and `String::new` as `const fn`s) - #64119 (ci: ensure all tool maintainers are assignable on issues) - #64444 (fix building libstd without backtrace feature) - #64446 (Fix build script sanitizer check.) - #64451 (when Miri tests are not passing, do not add Miri component) - #64467 (Hide diagnostics emitted during --cfg parsing) - #64497 (Don't print the "total" `-Ztime-passes` output if `--prints=...` is also given) - #64499 (Use `Symbol` in two more functions.) - #64504 (use println!() instead of println!("")) Failed merges: r? @ghost
This commit is contained in:
commit
a44881d892
@ -201,7 +201,9 @@ dependencies = [
|
||||
name = "build-manifest"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
]
|
||||
|
||||
|
@ -2000,6 +2000,8 @@ impl Step for HashSign {
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder<'_>) {
|
||||
// This gets called by `promote-release`
|
||||
// (https://github.com/rust-lang/rust-central-station/tree/master/promote-release).
|
||||
let mut cmd = builder.tool_cmd(Tool::BuildManifest);
|
||||
if builder.config.dry_run {
|
||||
return;
|
||||
@ -2010,10 +2012,14 @@ impl Step for HashSign {
|
||||
let addr = builder.config.dist_upload_addr.as_ref().unwrap_or_else(|| {
|
||||
panic!("\n\nfailed to specify `dist.upload-addr` in `config.toml`\n\n")
|
||||
});
|
||||
let file = builder.config.dist_gpg_password_file.as_ref().unwrap_or_else(|| {
|
||||
panic!("\n\nfailed to specify `dist.gpg-password-file` in `config.toml`\n\n")
|
||||
});
|
||||
let pass = t!(fs::read_to_string(&file));
|
||||
let pass = if env::var("BUILD_MANIFEST_DISABLE_SIGNING").is_err() {
|
||||
let file = builder.config.dist_gpg_password_file.as_ref().unwrap_or_else(|| {
|
||||
panic!("\n\nfailed to specify `dist.gpg-password-file` in `config.toml`\n\n")
|
||||
});
|
||||
t!(fs::read_to_string(&file))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let today = output(Command::new("date").arg("+%Y-%m-%d"));
|
||||
|
||||
|
@ -147,8 +147,15 @@ steps:
|
||||
git clone --depth=1 https://github.com/rust-lang-nursery/rust-toolstate.git
|
||||
cd rust-toolstate
|
||||
python2.7 "$BUILD_SOURCESDIRECTORY/src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" "$(git log --format=%s -n1 HEAD)" "" ""
|
||||
# Only check maintainers if this build is supposed to publish toolstate.
|
||||
# Builds that are not supposed to publish don't have the access token.
|
||||
if [ -n "${TOOLSTATE_PUBLISH+is_set}" ]; then
|
||||
TOOLSTATE_VALIDATE_MAINTAINERS_REPO=rust-lang/rust python2.7 "${BUILD_SOURCESDIRECTORY}/src/tools/publish_toolstate.py"
|
||||
fi
|
||||
cd ..
|
||||
rm -rf rust-toolstate
|
||||
env:
|
||||
TOOLSTATE_REPO_ACCESS_TOKEN: $(TOOLSTATE_REPO_ACCESS_TOKEN)
|
||||
condition: and(succeeded(), not(variables.SKIP_JOB), eq(variables['IMAGE'], 'mingw-check'))
|
||||
displayName: Verify the publish_toolstate script works
|
||||
|
||||
|
@ -117,7 +117,7 @@
|
||||
#![feature(allocator_internals)]
|
||||
#![feature(on_unimplemented)]
|
||||
#![feature(rustc_const_unstable)]
|
||||
#![feature(const_vec_new)]
|
||||
#![cfg_attr(bootstrap, feature(const_vec_new))]
|
||||
#![feature(slice_partition_dedup)]
|
||||
#![feature(maybe_uninit_extra, maybe_uninit_slice)]
|
||||
#![feature(alloc_layout_extra)]
|
||||
|
@ -113,13 +113,38 @@ impl<T, A: Alloc> RawVec<T, A> {
|
||||
}
|
||||
|
||||
impl<T> RawVec<T, Global> {
|
||||
/// HACK(Centril): This exists because `#[unstable]` `const fn`s needn't conform
|
||||
/// to `min_const_fn` and so they cannot be called in `min_const_fn`s either.
|
||||
///
|
||||
/// If you change `RawVec<T>::new` or dependencies, please take care to not
|
||||
/// introduce anything that would truly violate `min_const_fn`.
|
||||
///
|
||||
/// NOTE: We could avoid this hack and check conformance with some
|
||||
/// `#[rustc_force_min_const_fn]` attribute which requires conformance
|
||||
/// with `min_const_fn` but does not necessarily allow calling it in
|
||||
/// `stable(...) const fn` / user code not enabling `foo` when
|
||||
/// `#[rustc_const_unstable(feature = "foo", ..)]` is present.
|
||||
pub const NEW: Self = Self::new();
|
||||
|
||||
/// Creates the biggest possible `RawVec` (on the system heap)
|
||||
/// without allocating. If `T` has positive size, then this makes a
|
||||
/// `RawVec` with capacity `0`. If `T` is zero-sized, then it makes a
|
||||
/// `RawVec` with capacity `usize::MAX`. Useful for implementing
|
||||
/// delayed allocation.
|
||||
pub const fn new() -> Self {
|
||||
Self::new_in(Global)
|
||||
// FIXME(Centril): Reintegrate this with `fn new_in` when we can.
|
||||
|
||||
// `!0` is `usize::MAX`. This branch should be stripped at compile time.
|
||||
// FIXME(mark-i-m): use this line when `if`s are allowed in `const`:
|
||||
//let cap = if mem::size_of::<T>() == 0 { !0 } else { 0 };
|
||||
|
||||
// `Unique::empty()` doubles as "unallocated" and "zero-sized allocation".
|
||||
RawVec {
|
||||
ptr: Unique::empty(),
|
||||
// FIXME(mark-i-m): use `cap` when ifs are allowed in const
|
||||
cap: [0, !0][(mem::size_of::<T>() == 0) as usize],
|
||||
a: Global,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `RawVec` (on the system heap) with exactly the
|
||||
|
@ -369,7 +369,7 @@ impl String {
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_string_new")]
|
||||
#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_string_new"))]
|
||||
pub const fn new() -> String {
|
||||
String { vec: Vec::new() }
|
||||
}
|
||||
|
@ -314,10 +314,10 @@ impl<T> Vec<T> {
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_vec_new")]
|
||||
#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_vec_new"))]
|
||||
pub const fn new() -> Vec<T> {
|
||||
Vec {
|
||||
buf: RawVec::new(),
|
||||
buf: RawVec::NEW,
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use crate::session::{early_error, early_warn, Session};
|
||||
use crate::session::search_paths::SearchPath;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
||||
use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel};
|
||||
use rustc_target::spec::{Target, TargetTriple};
|
||||
@ -19,6 +20,7 @@ use syntax::parse::{ParseSess, new_parser_from_source_str};
|
||||
use syntax::parse::token;
|
||||
use syntax::symbol::{sym, Symbol};
|
||||
use syntax::feature_gate::UnstableFeatures;
|
||||
use syntax::source_map::SourceMap;
|
||||
|
||||
use errors::emitter::HumanReadableErrorType;
|
||||
use errors::{ColorConfig, FatalError, Handler};
|
||||
@ -1850,11 +1852,20 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
|
||||
opts
|
||||
}
|
||||
|
||||
struct NullEmitter;
|
||||
|
||||
impl errors::emitter::Emitter for NullEmitter {
|
||||
fn emit_diagnostic(&mut self, _: &errors::DiagnosticBuilder<'_>) {}
|
||||
}
|
||||
|
||||
// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.
|
||||
pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> {
|
||||
syntax::with_default_globals(move || {
|
||||
let cfg = cfgspecs.into_iter().map(|s| {
|
||||
let sess = ParseSess::new(FilePathMapping::empty());
|
||||
|
||||
let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let handler = Handler::with_emitter(false, None, Box::new(NullEmitter));
|
||||
let sess = ParseSess::with_span_handler(handler, cm);
|
||||
let filename = FileName::cfg_spec_source_code(&s);
|
||||
let mut parser = new_parser_from_source_str(&sess, filename, s.to_string());
|
||||
|
||||
|
@ -4,6 +4,7 @@ use build_helper::sanitizer_lib_boilerplate;
|
||||
use cmake::Config;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
|
||||
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
|
||||
return;
|
||||
}
|
||||
|
@ -226,21 +226,21 @@ impl CodegenBackend for LlvmCodegenBackend {
|
||||
for &(name, _) in back::write::RELOC_MODEL_ARGS.iter() {
|
||||
println!(" {}", name);
|
||||
}
|
||||
println!("");
|
||||
println!();
|
||||
}
|
||||
PrintRequest::CodeModels => {
|
||||
println!("Available code models:");
|
||||
for &(name, _) in back::write::CODE_GEN_MODEL_ARGS.iter(){
|
||||
println!(" {}", name);
|
||||
}
|
||||
println!("");
|
||||
println!();
|
||||
}
|
||||
PrintRequest::TlsModels => {
|
||||
println!("Available TLS models:");
|
||||
for &(name, _) in back::write::TLS_MODEL_ARGS.iter(){
|
||||
println!(" {}", name);
|
||||
}
|
||||
println!("");
|
||||
println!();
|
||||
}
|
||||
req => llvm_util::print(req, sess),
|
||||
}
|
||||
|
@ -134,8 +134,11 @@ pub struct TimePassesCallbacks {
|
||||
|
||||
impl Callbacks for TimePassesCallbacks {
|
||||
fn config(&mut self, config: &mut interface::Config) {
|
||||
// If a --prints=... option has been given, we don't print the "total"
|
||||
// time because it will mess up the --prints output. See #64339.
|
||||
self.time_passes =
|
||||
config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time;
|
||||
config.opts.prints.is_empty() &&
|
||||
(config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ use build_helper::sanitizer_lib_boilerplate;
|
||||
use cmake::Config;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
|
||||
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
|
||||
return;
|
||||
}
|
||||
|
@ -134,9 +134,8 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
||||
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
|
||||
cid: GlobalId<'tcx>,
|
||||
body: &'mir mir::Body<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
|
||||
debug!("eval_body_using_ecx: {:?}, {:?}", cid, param_env);
|
||||
debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env);
|
||||
let tcx = ecx.tcx.tcx;
|
||||
let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?;
|
||||
assert!(!layout.is_unsized());
|
||||
@ -162,7 +161,6 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
||||
ecx,
|
||||
cid.instance.def_id(),
|
||||
ret,
|
||||
param_env,
|
||||
)?;
|
||||
|
||||
debug!("eval_body_using_ecx done: {:?}", *ret);
|
||||
@ -658,7 +656,7 @@ pub fn const_eval_raw_provider<'tcx>(
|
||||
|
||||
let res = ecx.load_mir(cid.instance.def, cid.promoted);
|
||||
res.and_then(
|
||||
|body| eval_body_using_ecx(&mut ecx, cid, body, key.param_env)
|
||||
|body| eval_body_using_ecx(&mut ecx, cid, body)
|
||||
).and_then(|place| {
|
||||
Ok(RawConst {
|
||||
alloc_id: place.ptr.assert_ptr().alloc_id,
|
||||
|
@ -3,7 +3,7 @@
|
||||
//! After a const evaluation has computed a value, before we destroy the const evaluator's session
|
||||
//! memory, we need to extract all memory allocations to the global memory pool so they stay around.
|
||||
|
||||
use rustc::ty::{Ty, TyCtxt, ParamEnv, self};
|
||||
use rustc::ty::{Ty, self};
|
||||
use rustc::mir::interpret::{InterpResult, ErrorHandled};
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::DefId;
|
||||
@ -11,32 +11,29 @@ use super::validity::RefTracking;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
||||
use syntax::ast::Mutability;
|
||||
use syntax_pos::Span;
|
||||
|
||||
use super::{
|
||||
ValueVisitor, MemoryKind, Pointer, AllocId, MPlaceTy, Scalar,
|
||||
ValueVisitor, MemoryKind, AllocId, MPlaceTy, Scalar,
|
||||
};
|
||||
use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};
|
||||
|
||||
struct InternVisitor<'rt, 'mir, 'tcx> {
|
||||
/// previously encountered safe references
|
||||
ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
|
||||
/// The ectx from which we intern.
|
||||
ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
/// Previously encountered safe references.
|
||||
ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
|
||||
/// A list of all encountered allocations. After type-based interning, we traverse this list to
|
||||
/// also intern allocations that are only referenced by a raw pointer or inside a union.
|
||||
leftover_allocations: &'rt mut FxHashSet<AllocId>,
|
||||
/// The root node of the value that we're looking at. This field is never mutated and only used
|
||||
/// for sanity assertions that will ICE when `const_qualif` screws up.
|
||||
mode: InternMode,
|
||||
/// This field stores the mutability of the value *currently* being checked.
|
||||
/// It is set to mutable when an `UnsafeCell` is encountered
|
||||
/// When recursing across a reference, we don't recurse but store the
|
||||
/// value to be checked in `ref_tracking` together with the mutability at which we are checking
|
||||
/// the value.
|
||||
/// When encountering an immutable reference, we treat everything as immutable that is behind
|
||||
/// it.
|
||||
/// When encountering a mutable reference, we determine the pointee mutability
|
||||
/// taking into account the mutability of the context: `& &mut i32` is entirely immutable,
|
||||
/// despite the nested mutable reference!
|
||||
/// The field gets updated when an `UnsafeCell` is encountered.
|
||||
mutability: Mutability,
|
||||
/// A list of all encountered relocations. After type-based interning, we traverse this list to
|
||||
/// also intern allocations that are only referenced by a raw pointer or inside a union.
|
||||
leftover_relocations: &'rt mut FxHashSet<AllocId>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
|
||||
@ -45,9 +42,10 @@ enum InternMode {
|
||||
/// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
|
||||
/// that will actually be treated as mutable.
|
||||
Static,
|
||||
/// UnsafeCell is OK in the value of a constant, but not behind references in a constant
|
||||
/// UnsafeCell is OK in the value of a constant: `const FOO = Cell::new(0)` creates
|
||||
/// a new cell every time it is used.
|
||||
ConstBase,
|
||||
/// `UnsafeCell` ICEs
|
||||
/// `UnsafeCell` ICEs.
|
||||
Const,
|
||||
}
|
||||
|
||||
@ -55,48 +53,100 @@ enum InternMode {
|
||||
/// into the memory of other constants or statics
|
||||
struct IsStaticOrFn;
|
||||
|
||||
/// Intern an allocation without looking at its children.
|
||||
/// `mode` is the mode of the environment where we found this pointer.
|
||||
/// `mutablity` is the mutability of the place to be interned; even if that says
|
||||
/// `immutable` things might become mutable if `ty` is not frozen.
|
||||
/// `ty` can be `None` if there is no potential interior mutability
|
||||
/// to account for (e.g. for vtables).
|
||||
fn intern_shallow<'rt, 'mir, 'tcx>(
|
||||
ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
|
||||
leftover_allocations: &'rt mut FxHashSet<AllocId>,
|
||||
mode: InternMode,
|
||||
alloc_id: AllocId,
|
||||
mutability: Mutability,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
|
||||
trace!(
|
||||
"InternVisitor::intern {:?} with {:?}",
|
||||
alloc_id, mutability,
|
||||
);
|
||||
// remove allocation
|
||||
let tcx = ecx.tcx;
|
||||
let memory = ecx.memory_mut();
|
||||
let (kind, mut alloc) = match memory.alloc_map.remove(&alloc_id) {
|
||||
Some(entry) => entry,
|
||||
None => {
|
||||
// Pointer not found in local memory map. It is either a pointer to the global
|
||||
// map, or dangling.
|
||||
// If the pointer is dangling (neither in local nor global memory), we leave it
|
||||
// to validation to error. The `delay_span_bug` ensures that we don't forget such
|
||||
// a check in validation.
|
||||
if tcx.alloc_map.lock().get(alloc_id).is_none() {
|
||||
tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer");
|
||||
}
|
||||
// treat dangling pointers like other statics
|
||||
// just to stop trying to recurse into them
|
||||
return Ok(Some(IsStaticOrFn));
|
||||
},
|
||||
};
|
||||
// This match is just a canary for future changes to `MemoryKind`, which most likely need
|
||||
// changes in this function.
|
||||
match kind {
|
||||
MemoryKind::Stack | MemoryKind::Vtable => {},
|
||||
}
|
||||
// Set allocation mutability as appropriate. This is used by LLVM to put things into
|
||||
// read-only memory, and also by Miri when evluating other constants/statics that
|
||||
// access this one.
|
||||
if mode == InternMode::Static {
|
||||
// When `ty` is `None`, we assume no interior mutability.
|
||||
let frozen = ty.map_or(true, |ty| ty.is_freeze(
|
||||
ecx.tcx.tcx,
|
||||
ecx.param_env,
|
||||
ecx.tcx.span,
|
||||
));
|
||||
// For statics, allocation mutability is the combination of the place mutability and
|
||||
// the type mutability.
|
||||
// The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere.
|
||||
if mutability == Mutability::Immutable && frozen {
|
||||
alloc.mutability = Mutability::Immutable;
|
||||
} else {
|
||||
// Just making sure we are not "upgrading" an immutable allocation to mutable.
|
||||
assert_eq!(alloc.mutability, Mutability::Mutable);
|
||||
}
|
||||
} else {
|
||||
// We *could* be non-frozen at `ConstBase`, for constants like `Cell::new(0)`.
|
||||
// But we still intern that as immutable as the memory cannot be changed once the
|
||||
// initial value was computed.
|
||||
// Constants are never mutable.
|
||||
assert_eq!(
|
||||
mutability, Mutability::Immutable,
|
||||
"Something went very wrong: mutability requested for a constant"
|
||||
);
|
||||
alloc.mutability = Mutability::Immutable;
|
||||
};
|
||||
// link the alloc id to the actual allocation
|
||||
let alloc = tcx.intern_const_alloc(alloc);
|
||||
leftover_allocations.extend(alloc.relocations().iter().map(|&(_, ((), reloc))| reloc));
|
||||
tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
impl<'rt, 'mir, 'tcx> InternVisitor<'rt, 'mir, 'tcx> {
|
||||
/// Intern an allocation without looking at its children
|
||||
fn intern_shallow(
|
||||
&mut self,
|
||||
ptr: Pointer,
|
||||
alloc_id: AllocId,
|
||||
mutability: Mutability,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
|
||||
trace!(
|
||||
"InternVisitor::intern {:?} with {:?}",
|
||||
ptr, mutability,
|
||||
);
|
||||
// remove allocation
|
||||
let tcx = self.ecx.tcx;
|
||||
let memory = self.ecx.memory_mut();
|
||||
let (kind, mut alloc) = match memory.alloc_map.remove(&ptr.alloc_id) {
|
||||
Some(entry) => entry,
|
||||
None => {
|
||||
// if the pointer is dangling (neither in local nor global memory), we leave it
|
||||
// to validation to error. The `delay_span_bug` ensures that we don't forget such
|
||||
// a check in validation.
|
||||
if tcx.alloc_map.lock().get(ptr.alloc_id).is_none() {
|
||||
tcx.sess.delay_span_bug(self.ecx.tcx.span, "tried to intern dangling pointer");
|
||||
}
|
||||
// treat dangling pointers like other statics
|
||||
// just to stop trying to recurse into them
|
||||
return Ok(Some(IsStaticOrFn));
|
||||
},
|
||||
};
|
||||
// This match is just a canary for future changes to `MemoryKind`, which most likely need
|
||||
// changes in this function.
|
||||
match kind {
|
||||
MemoryKind::Stack | MemoryKind::Vtable => {},
|
||||
}
|
||||
// Ensure llvm knows to only put this into immutable memory if the value is immutable either
|
||||
// by being behind a reference or by being part of a static or const without interior
|
||||
// mutability
|
||||
alloc.mutability = mutability;
|
||||
// link the alloc id to the actual allocation
|
||||
let alloc = tcx.intern_const_alloc(alloc);
|
||||
self.leftover_relocations.extend(alloc.relocations().iter().map(|&(_, ((), reloc))| reloc));
|
||||
tcx.alloc_map.lock().set_alloc_id_memory(ptr.alloc_id, alloc);
|
||||
Ok(None)
|
||||
intern_shallow(
|
||||
self.ecx,
|
||||
self.leftover_allocations,
|
||||
self.mode,
|
||||
alloc_id,
|
||||
mutability,
|
||||
ty,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,14 +169,16 @@ for
|
||||
) -> InterpResult<'tcx> {
|
||||
if let Some(def) = mplace.layout.ty.ty_adt_def() {
|
||||
if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
|
||||
// We are crossing over an `UnsafeCell`, we can mutate again
|
||||
// We are crossing over an `UnsafeCell`, we can mutate again. This means that
|
||||
// References we encounter inside here are interned as pointing to mutable
|
||||
// allocations.
|
||||
let old = std::mem::replace(&mut self.mutability, Mutability::Mutable);
|
||||
assert_ne!(
|
||||
self.mode, InternMode::Const,
|
||||
"UnsafeCells are not allowed behind references in constants. This should have \
|
||||
been prevented statically by const qualification. If this were allowed one \
|
||||
would be able to change a constant at one use site and other use sites may \
|
||||
arbitrarily decide to change, too.",
|
||||
would be able to change a constant at one use site and other use sites could \
|
||||
observe that mutation.",
|
||||
);
|
||||
let walked = self.walk_aggregate(mplace, fields);
|
||||
self.mutability = old;
|
||||
@ -145,12 +197,13 @@ for
|
||||
// Handle trait object vtables
|
||||
if let Ok(meta) = value.to_meta() {
|
||||
if let ty::Dynamic(..) =
|
||||
self.ecx.tcx.struct_tail_erasing_lifetimes(referenced_ty, self.param_env).sty
|
||||
self.ecx.tcx.struct_tail_erasing_lifetimes(
|
||||
referenced_ty, self.ecx.param_env).sty
|
||||
{
|
||||
if let Ok(vtable) = meta.unwrap().to_ptr() {
|
||||
// explitly choose `Immutable` here, since vtables are immutable, even
|
||||
// if the reference of the fat pointer is mutable
|
||||
self.intern_shallow(vtable, Mutability::Immutable)?;
|
||||
self.intern_shallow(vtable.alloc_id, Mutability::Immutable, None)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,7 +230,7 @@ for
|
||||
(InternMode::Const, hir::Mutability::MutMutable) => {
|
||||
match referenced_ty.sty {
|
||||
ty::Array(_, n)
|
||||
if n.eval_usize(self.ecx.tcx.tcx, self.param_env) == 0 => {}
|
||||
if n.eval_usize(self.ecx.tcx.tcx, self.ecx.param_env) == 0 => {}
|
||||
ty::Slice(_)
|
||||
if value.to_meta().unwrap().unwrap().to_usize(self.ecx)? == 0 => {}
|
||||
_ => bug!("const qualif failed to prevent mutable references"),
|
||||
@ -195,21 +248,13 @@ for
|
||||
(Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable,
|
||||
_ => Mutability::Immutable,
|
||||
};
|
||||
// Compute the mutability of the allocation
|
||||
let intern_mutability = intern_mutability(
|
||||
self.ecx.tcx.tcx,
|
||||
self.param_env,
|
||||
mplace.layout.ty,
|
||||
self.ecx.tcx.span,
|
||||
mutability,
|
||||
);
|
||||
// Recursing behind references changes the intern mode for constants in order to
|
||||
// cause assertions to trigger if we encounter any `UnsafeCell`s.
|
||||
let mode = match self.mode {
|
||||
InternMode::ConstBase => InternMode::Const,
|
||||
other => other,
|
||||
};
|
||||
match self.intern_shallow(ptr, intern_mutability)? {
|
||||
match self.intern_shallow(ptr.alloc_id, mutability, Some(mplace.layout.ty))? {
|
||||
// No need to recurse, these are interned already and statics may have
|
||||
// cycles, so we don't want to recurse there
|
||||
Some(IsStaticOrFn) => {},
|
||||
@ -224,69 +269,45 @@ for
|
||||
}
|
||||
}
|
||||
|
||||
/// Figure out the mutability of the allocation.
|
||||
/// Mutable if it has interior mutability *anywhere* in the type.
|
||||
fn intern_mutability<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
mutability: Mutability,
|
||||
) -> Mutability {
|
||||
let has_interior_mutability = !ty.is_freeze(tcx, param_env, span);
|
||||
if has_interior_mutability {
|
||||
Mutability::Mutable
|
||||
} else {
|
||||
mutability
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intern_const_alloc_recursive(
|
||||
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
|
||||
def_id: DefId,
|
||||
ret: MPlaceTy<'tcx>,
|
||||
// FIXME(oli-obk): can we scrap the param env? I think we can, the final value of a const eval
|
||||
// must always be monomorphic, right?
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let tcx = ecx.tcx;
|
||||
// this `mutability` is the mutability of the place, ignoring the type
|
||||
let (mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
|
||||
let (base_mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
|
||||
Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static),
|
||||
None => (Mutability::Immutable, InternMode::ConstBase),
|
||||
// `static mut` doesn't care about interior mutability, it's mutable anyway
|
||||
Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static),
|
||||
// consts, promoteds. FIXME: what about array lengths, array initializers?
|
||||
None => (Mutability::Immutable, InternMode::ConstBase),
|
||||
};
|
||||
|
||||
// type based interning
|
||||
let mut ref_tracking = RefTracking::new((ret, mutability, base_intern_mode));
|
||||
let leftover_relocations = &mut FxHashSet::default();
|
||||
|
||||
// This mutability is the combination of the place mutability and the type mutability. If either
|
||||
// is mutable, `alloc_mutability` is mutable. This exists because the entire allocation needs
|
||||
// to be mutable if it contains an `UnsafeCell` anywhere. The other `mutability` exists so that
|
||||
// the visitor does not treat everything outside the `UnsafeCell` as mutable.
|
||||
let alloc_mutability = intern_mutability(
|
||||
tcx.tcx, param_env, ret.layout.ty, tcx.span, mutability,
|
||||
);
|
||||
// Type based interning.
|
||||
// `ref_tracking` tracks typed references we have seen and still need to crawl for
|
||||
// more typed information inside them.
|
||||
// `leftover_allocations` collects *all* allocations we see, because some might not
|
||||
// be available in a typed way. They get interned at the end.
|
||||
let mut ref_tracking = RefTracking::new((ret, base_mutability, base_intern_mode));
|
||||
let leftover_allocations = &mut FxHashSet::default();
|
||||
|
||||
// start with the outermost allocation
|
||||
InternVisitor {
|
||||
ref_tracking: &mut ref_tracking,
|
||||
intern_shallow(
|
||||
ecx,
|
||||
mode: base_intern_mode,
|
||||
leftover_relocations,
|
||||
param_env,
|
||||
mutability,
|
||||
}.intern_shallow(ret.ptr.to_ptr()?, alloc_mutability)?;
|
||||
leftover_allocations,
|
||||
base_intern_mode,
|
||||
ret.ptr.to_ptr()?.alloc_id,
|
||||
base_mutability,
|
||||
Some(ret.layout.ty)
|
||||
)?;
|
||||
|
||||
while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
|
||||
let interned = InternVisitor {
|
||||
ref_tracking: &mut ref_tracking,
|
||||
ecx,
|
||||
mode,
|
||||
leftover_relocations,
|
||||
param_env,
|
||||
leftover_allocations,
|
||||
mutability,
|
||||
}.visit_value(mplace);
|
||||
if let Err(error) = interned {
|
||||
@ -309,15 +330,23 @@ pub fn intern_const_alloc_recursive(
|
||||
// Intern the rest of the allocations as mutable. These might be inside unions, padding, raw
|
||||
// pointers, ... So we can't intern them according to their type rules
|
||||
|
||||
let mut todo: Vec<_> = leftover_relocations.iter().cloned().collect();
|
||||
let mut todo: Vec<_> = leftover_allocations.iter().cloned().collect();
|
||||
while let Some(alloc_id) = todo.pop() {
|
||||
if let Some((_, alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) {
|
||||
// We can't call the `intern` method here, as its logic is tailored to safe references.
|
||||
// So we hand-roll the interning logic here again
|
||||
if let Some((_, mut alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) {
|
||||
// We can't call the `intern_shallow` method here, as its logic is tailored to safe
|
||||
// references and a `leftover_allocations` set (where we only have a todo-list here).
|
||||
// So we hand-roll the interning logic here again.
|
||||
if base_intern_mode != InternMode::Static {
|
||||
// If it's not a static, it *must* be immutable.
|
||||
// We cannot have mutable memory inside a constant.
|
||||
// FIXME: ideally we would assert that they already are immutable, to double-
|
||||
// check our static checks.
|
||||
alloc.mutability = Mutability::Immutable;
|
||||
}
|
||||
let alloc = tcx.intern_const_alloc(alloc);
|
||||
tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
|
||||
for &(_, ((), reloc)) in alloc.relocations().iter() {
|
||||
if leftover_relocations.insert(reloc) {
|
||||
if leftover_allocations.insert(reloc) {
|
||||
todo.push(reloc);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use build_helper::sanitizer_lib_boilerplate;
|
||||
use cmake::Config;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
|
||||
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
|
||||
return;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use build_helper::sanitizer_lib_boilerplate;
|
||||
use cmake::Config;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
|
||||
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
|
||||
return;
|
||||
}
|
||||
|
@ -25,17 +25,11 @@ profiler_builtins = { path = "../libprofiler_builtins", optional = true }
|
||||
unwind = { path = "../libunwind" }
|
||||
hashbrown = { version = "0.5.0", features = ['rustc-dep-of-std'] }
|
||||
|
||||
[dependencies.backtrace]
|
||||
[dependencies.backtrace_rs]
|
||||
package = "backtrace"
|
||||
version = "0.3.37"
|
||||
default-features = false # don't use coresymbolication on OSX
|
||||
features = [
|
||||
"rustc-dep-of-std", # enable build support for integrating into libstd
|
||||
"dbghelp", # backtrace/symbolize on MSVC
|
||||
"libbacktrace", # symbolize on most platforms
|
||||
"libunwind", # backtrace on most platforms
|
||||
"dladdr", # symbolize on platforms w/o libbacktrace
|
||||
]
|
||||
optional = true
|
||||
default-features = false # without the libstd `backtrace` feature, stub out everything
|
||||
features = [ "rustc-dep-of-std" ] # enable build support for integrating into libstd
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.7"
|
||||
@ -65,6 +59,13 @@ cc = "1.0"
|
||||
[features]
|
||||
default = ["std_detect_file_io", "std_detect_dlsym_getauxval"]
|
||||
|
||||
backtrace = [
|
||||
"backtrace_rs/dbghelp", # backtrace/symbolize on MSVC
|
||||
"backtrace_rs/libbacktrace", # symbolize on most platforms
|
||||
"backtrace_rs/libunwind", # backtrace on most platforms
|
||||
"backtrace_rs/dladdr", # symbolize on platforms w/o libbacktrace
|
||||
]
|
||||
|
||||
panic-unwind = ["panic_unwind"]
|
||||
profiler = ["profiler_builtins"]
|
||||
compiler-builtins-c = ["alloc/compiler-builtins-c"]
|
||||
|
@ -97,6 +97,7 @@ use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
use crate::sync::Mutex;
|
||||
use crate::sys_common::backtrace::{output_filename, lock};
|
||||
use crate::vec::Vec;
|
||||
use backtrace_rs as backtrace;
|
||||
use backtrace::BytesOrWideString;
|
||||
|
||||
/// A captured OS thread stack backtrace.
|
||||
|
@ -17,8 +17,7 @@ use crate::ptr;
|
||||
use crate::raw;
|
||||
use crate::sys::stdio::panic_output;
|
||||
use crate::sys_common::rwlock::RWLock;
|
||||
use crate::sys_common::thread_info;
|
||||
use crate::sys_common::util;
|
||||
use crate::sys_common::{thread_info, util, backtrace};
|
||||
use crate::thread;
|
||||
|
||||
#[cfg(not(test))]
|
||||
@ -157,20 +156,18 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
|
||||
}
|
||||
|
||||
fn default_hook(info: &PanicInfo<'_>) {
|
||||
#[cfg(feature = "backtrace")]
|
||||
use crate::sys_common::{backtrace as backtrace_mod};
|
||||
|
||||
// If this is a double panic, make sure that we print a backtrace
|
||||
// for this panic. Otherwise only print it if logging is enabled.
|
||||
#[cfg(feature = "backtrace")]
|
||||
let log_backtrace = {
|
||||
let log_backtrace = if cfg!(feature = "backtrace") {
|
||||
let panics = update_panic_count(0);
|
||||
|
||||
if panics >= 2 {
|
||||
Some(backtrace::PrintFmt::Full)
|
||||
Some(backtrace_rs::PrintFmt::Full)
|
||||
} else {
|
||||
backtrace_mod::log_enabled()
|
||||
backtrace::log_enabled()
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// The current implementation always returns `Some`.
|
||||
@ -190,14 +187,13 @@ fn default_hook(info: &PanicInfo<'_>) {
|
||||
let _ = writeln!(err, "thread '{}' panicked at '{}', {}",
|
||||
name, msg, location);
|
||||
|
||||
#[cfg(feature = "backtrace")]
|
||||
{
|
||||
if cfg!(feature = "backtrace") {
|
||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
|
||||
|
||||
if let Some(format) = log_backtrace {
|
||||
let _ = backtrace_mod::print(err, format);
|
||||
let _ = backtrace::print(err, format);
|
||||
} else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
|
||||
let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \
|
||||
environment variable to display a backtrace.");
|
||||
|
@ -422,7 +422,7 @@ impl fmt::Debug for ChildStderr {
|
||||
/// // Execute `ls` in the current directory of the program.
|
||||
/// list_dir.status().expect("process failed to execute");
|
||||
///
|
||||
/// println!("");
|
||||
/// println!();
|
||||
///
|
||||
/// // Change `ls` to execute in the root directory.
|
||||
/// list_dir.current_dir("/");
|
||||
|
@ -7,10 +7,9 @@ use crate::io;
|
||||
use crate::borrow::Cow;
|
||||
use crate::io::prelude::*;
|
||||
use crate::path::{self, Path, PathBuf};
|
||||
use crate::sync::atomic::{self, Ordering};
|
||||
use crate::sys::mutex::Mutex;
|
||||
|
||||
use backtrace::{BacktraceFmt, BytesOrWideString, PrintFmt};
|
||||
use backtrace_rs::{BacktraceFmt, BytesOrWideString, PrintFmt};
|
||||
|
||||
/// Max number of frames to print.
|
||||
const MAX_NB_FRAMES: usize = 100;
|
||||
@ -74,14 +73,14 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
|
||||
bt_fmt.add_context()?;
|
||||
let mut idx = 0;
|
||||
let mut res = Ok(());
|
||||
backtrace::trace_unsynchronized(|frame| {
|
||||
backtrace_rs::trace_unsynchronized(|frame| {
|
||||
if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut hit = false;
|
||||
let mut stop = false;
|
||||
backtrace::resolve_frame_unsynchronized(frame, |symbol| {
|
||||
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
|
||||
hit = true;
|
||||
if print_fmt == PrintFmt::Short {
|
||||
if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
|
||||
@ -130,6 +129,8 @@ where
|
||||
// For now logging is turned off by default, and this function checks to see
|
||||
// whether the magical environment variable is present to see if it's turned on.
|
||||
pub fn log_enabled() -> Option<PrintFmt> {
|
||||
use crate::sync::atomic::{self, Ordering};
|
||||
|
||||
// Setting environment variables for Fuchsia components isn't a standard
|
||||
// or easily supported workflow. For now, always display backtraces.
|
||||
if cfg!(target_os = "fuchsia") {
|
||||
|
@ -41,7 +41,6 @@ macro_rules! rtunwrap {
|
||||
|
||||
pub mod alloc;
|
||||
pub mod at_exit_imp;
|
||||
#[cfg(feature = "backtrace")]
|
||||
pub mod backtrace;
|
||||
pub mod condvar;
|
||||
pub mod io;
|
||||
|
@ -877,9 +877,9 @@ fn check_matcher_core(
|
||||
// Now `last` holds the complete set of NT tokens that could
|
||||
// end the sequence before SUFFIX. Check that every one works with `suffix`.
|
||||
'each_last: for token in &last.tokens {
|
||||
if let TokenTree::MetaVarDecl(_, ref name, ref frag_spec) = *token {
|
||||
if let TokenTree::MetaVarDecl(_, name, frag_spec) = *token {
|
||||
for next_token in &suffix_first.tokens {
|
||||
match is_in_follow(next_token, &frag_spec.as_str()) {
|
||||
match is_in_follow(next_token, frag_spec.name) {
|
||||
IsInFollow::Invalid(msg, help) => {
|
||||
sess.span_diagnostic
|
||||
.struct_span_err(next_token.span(), &msg)
|
||||
@ -948,7 +948,7 @@ fn check_matcher_core(
|
||||
|
||||
fn token_can_be_followed_by_any(tok: "ed::TokenTree) -> bool {
|
||||
if let quoted::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok {
|
||||
frag_can_be_followed_by_any(&frag_spec.as_str())
|
||||
frag_can_be_followed_by_any(frag_spec.name)
|
||||
} else {
|
||||
// (Non NT's can always be followed by anthing in matchers.)
|
||||
true
|
||||
@ -963,15 +963,15 @@ fn token_can_be_followed_by_any(tok: "ed::TokenTree) -> bool {
|
||||
/// specifier which consumes at most one token tree can be followed by
|
||||
/// a fragment specifier (indeed, these fragments can be followed by
|
||||
/// ANYTHING without fear of future compatibility hazards).
|
||||
fn frag_can_be_followed_by_any(frag: &str) -> bool {
|
||||
fn frag_can_be_followed_by_any(frag: Symbol) -> bool {
|
||||
match frag {
|
||||
"item" | // always terminated by `}` or `;`
|
||||
"block" | // exactly one token tree
|
||||
"ident" | // exactly one token tree
|
||||
"literal" | // exactly one token tree
|
||||
"meta" | // exactly one token tree
|
||||
"lifetime" | // exactly one token tree
|
||||
"tt" => // exactly one token tree
|
||||
sym::item | // always terminated by `}` or `;`
|
||||
sym::block | // exactly one token tree
|
||||
sym::ident | // exactly one token tree
|
||||
sym::literal | // exactly one token tree
|
||||
sym::meta | // exactly one token tree
|
||||
sym::lifetime | // exactly one token tree
|
||||
sym::tt => // exactly one token tree
|
||||
true,
|
||||
|
||||
_ =>
|
||||
@ -993,7 +993,7 @@ enum IsInFollow {
|
||||
/// break macros that were relying on that binary operator as a
|
||||
/// separator.
|
||||
// when changing this do not forget to update doc/book/macros.md!
|
||||
fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> IsInFollow {
|
||||
fn is_in_follow(tok: "ed::TokenTree, frag: Symbol) -> IsInFollow {
|
||||
use quoted::TokenTree;
|
||||
|
||||
if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok {
|
||||
@ -1002,17 +1002,17 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> IsInFollow {
|
||||
IsInFollow::Yes
|
||||
} else {
|
||||
match frag {
|
||||
"item" => {
|
||||
sym::item => {
|
||||
// since items *must* be followed by either a `;` or a `}`, we can
|
||||
// accept anything after them
|
||||
IsInFollow::Yes
|
||||
}
|
||||
"block" => {
|
||||
sym::block => {
|
||||
// anything can follow block, the braces provide an easy boundary to
|
||||
// maintain
|
||||
IsInFollow::Yes
|
||||
}
|
||||
"stmt" | "expr" => {
|
||||
sym::stmt | sym::expr => {
|
||||
const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"];
|
||||
match tok {
|
||||
TokenTree::Token(token) => match token.kind {
|
||||
@ -1022,7 +1022,7 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> IsInFollow {
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
"pat" => {
|
||||
sym::pat => {
|
||||
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
|
||||
match tok {
|
||||
TokenTree::Token(token) => match token.kind {
|
||||
@ -1033,7 +1033,7 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> IsInFollow {
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
"path" | "ty" => {
|
||||
sym::path | sym::ty => {
|
||||
const TOKENS: &[&str] = &[
|
||||
"`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`",
|
||||
"`where`",
|
||||
@ -1061,20 +1061,20 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> IsInFollow {
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
"ident" | "lifetime" => {
|
||||
sym::ident | sym::lifetime => {
|
||||
// being a single token, idents and lifetimes are harmless
|
||||
IsInFollow::Yes
|
||||
}
|
||||
"literal" => {
|
||||
sym::literal => {
|
||||
// literals may be of a single token, or two tokens (negative numbers)
|
||||
IsInFollow::Yes
|
||||
}
|
||||
"meta" | "tt" => {
|
||||
sym::meta | sym::tt => {
|
||||
// being either a single token or a delimited sequence, tt is
|
||||
// harmless
|
||||
IsInFollow::Yes
|
||||
}
|
||||
"vis" => {
|
||||
sym::vis => {
|
||||
// Explicitly disallow `priv`, on the off chance it comes back.
|
||||
const TOKENS: &[&str] = &["`,`", "an ident", "a type"];
|
||||
match tok {
|
||||
@ -1099,7 +1099,7 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> IsInFollow {
|
||||
_ => IsInFollow::No(TOKENS),
|
||||
}
|
||||
}
|
||||
"" => IsInFollow::Yes, // kw::Invalid
|
||||
kw::Invalid => IsInFollow::Yes,
|
||||
_ => IsInFollow::Invalid(
|
||||
format!("invalid fragment specifier `{}`", frag),
|
||||
VALID_FRAGMENT_NAMES_MSG,
|
||||
|
@ -242,12 +242,12 @@ fn non_immediate_args(a: BigStruct, b: BigStruct) {
|
||||
|
||||
fn binding(a: i64, b: u64, c: f64) {
|
||||
let x = 0; // #break
|
||||
println!("")
|
||||
println!()
|
||||
}
|
||||
|
||||
fn assignment(mut a: u64, b: u64, c: f64) {
|
||||
a = b; // #break
|
||||
println!("")
|
||||
println!()
|
||||
}
|
||||
|
||||
fn function_call(x: u64, y: u64, z: f64) {
|
||||
|
@ -1,15 +1,11 @@
|
||||
// run-pass
|
||||
// check-pass
|
||||
|
||||
#![allow(dead_code)]
|
||||
// Test several functions can be used for constants
|
||||
// 1. Vec::new()
|
||||
// 2. String::new()
|
||||
|
||||
#![feature(const_vec_new)]
|
||||
#![feature(const_string_new)]
|
||||
|
||||
const MY_VEC: Vec<usize> = Vec::new();
|
||||
|
||||
const MY_STRING: String = String::new();
|
||||
|
||||
pub fn main() {}
|
||||
fn main() {}
|
||||
|
3
src/test/ui/conditional-compilation/cfg-arg-invalid-6.rs
Normal file
3
src/test/ui/conditional-compilation/cfg-arg-invalid-6.rs
Normal file
@ -0,0 +1,3 @@
|
||||
// compile-flags: --cfg a{
|
||||
// error-pattern: invalid `--cfg` argument: `a{` (expected `key` or `key="value"`)
|
||||
fn main() {}
|
@ -0,0 +1,2 @@
|
||||
error: invalid `--cfg` argument: `a{` (expected `key` or `key="value"`)
|
||||
|
@ -14,8 +14,9 @@ trait Bar<T, U: Foo<T>> {
|
||||
impl Foo<u32> for () {
|
||||
const X: u32 = 42;
|
||||
}
|
||||
|
||||
impl Foo<Vec<u32>> for String {
|
||||
const X: Vec<u32> = Vec::new(); //~ ERROR not yet stable as a const fn
|
||||
const X: Vec<u32> = Vec::new();
|
||||
}
|
||||
|
||||
impl Bar<u32, ()> for () {}
|
||||
|
@ -4,13 +4,5 @@ error[E0493]: destructors cannot be evaluated at compile-time
|
||||
LL | const F: u32 = (U::X, 42).1;
|
||||
| ^^^^^^^^^^ constants cannot evaluate destructors
|
||||
|
||||
error: `std::vec::Vec::<T>::new` is not yet stable as a const fn
|
||||
--> $DIR/feature-gate-unleash_the_miri_inside_of_you.rs:18:25
|
||||
|
|
||||
LL | const X: Vec<u32> = Vec::new();
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(const_vec_new)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
|
20
src/test/ui/consts/miri_unleashed/mutable_const.rs
Normal file
20
src/test/ui/consts/miri_unleashed/mutable_const.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// compile-flags: -Zunleash-the-miri-inside-of-you
|
||||
|
||||
#![feature(const_raw_ptr_deref)]
|
||||
#![deny(const_err)]
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
|
||||
// make sure we do not just intern this as mutable
|
||||
const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _;
|
||||
|
||||
const MUTATING_BEHIND_RAW: () = {
|
||||
// Test that `MUTABLE_BEHIND_RAW` is actually immutable, by doing this at const time.
|
||||
unsafe {
|
||||
*MUTABLE_BEHIND_RAW = 99 //~ WARN skipping const checks
|
||||
//~^ ERROR any use of this value will cause an error
|
||||
//~^^ tried to modify constant memory
|
||||
}
|
||||
};
|
||||
|
||||
fn main() {}
|
27
src/test/ui/consts/miri_unleashed/mutable_const.stderr
Normal file
27
src/test/ui/consts/miri_unleashed/mutable_const.stderr
Normal file
@ -0,0 +1,27 @@
|
||||
warning: skipping const checks
|
||||
--> $DIR/mutable_const.rs:14:9
|
||||
|
|
||||
LL | *MUTABLE_BEHIND_RAW = 99
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: any use of this value will cause an error
|
||||
--> $DIR/mutable_const.rs:14:9
|
||||
|
|
||||
LL | / const MUTATING_BEHIND_RAW: () = {
|
||||
LL | | // Test that `MUTABLE_BEHIND_RAW` is actually immutable, by doing this at const time.
|
||||
LL | | unsafe {
|
||||
LL | | *MUTABLE_BEHIND_RAW = 99
|
||||
| | ^^^^^^^^^^^^^^^^^^^^^^^^ tried to modify constant memory
|
||||
... |
|
||||
LL | | }
|
||||
LL | | };
|
||||
| |__-
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/mutable_const.rs:4:9
|
||||
|
|
||||
LL | #![deny(const_err)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -6,7 +6,7 @@ LL | *MUH.x.get() = 99;
|
||||
|
||||
thread 'rustc' panicked at 'assertion failed: `(left != right)`
|
||||
left: `Const`,
|
||||
right: `Const`: UnsafeCells are not allowed behind references in constants. This should have been prevented statically by const qualification. If this were allowed one would be able to change a constant at one use site and other use sites may arbitrarily decide to change, too.', src/librustc_mir/interpret/intern.rs:LL:CC
|
||||
right: `Const`: UnsafeCells are not allowed behind references in constants. This should have been prevented statically by const qualification. If this were allowed one would be able to change a constant at one use site and other use sites could observe that mutation.', src/librustc_mir/interpret/intern.rs:LL:CC
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
|
||||
|
||||
error: internal compiler error: unexpected panic
|
||||
|
@ -143,7 +143,7 @@ pub fn main() {
|
||||
v[0].descend_into_self(&mut c);
|
||||
assert!(!c.saw_prev_marked); // <-- different from below, b/c acyclic above
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 1: { v[0] -> v[1], v[1] -> v[0] };
|
||||
// does not exercise `v` itself
|
||||
@ -158,7 +158,7 @@ pub fn main() {
|
||||
v[0].descend_into_self(&mut c);
|
||||
assert!(c.saw_prev_marked);
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 2: { v[0] -> v, v[1] -> v }
|
||||
let v: V = Named::new("v");
|
||||
@ -171,7 +171,7 @@ pub fn main() {
|
||||
v.descend_into_self(&mut c);
|
||||
assert!(c.saw_prev_marked);
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 3: { hk0 -> hv0, hv0 -> hk0, hk1 -> hv1, hv1 -> hk1 };
|
||||
// does not exercise `h` itself
|
||||
@ -193,7 +193,7 @@ pub fn main() {
|
||||
assert!(c.saw_prev_marked);
|
||||
}
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 4: { h -> (hmk0,hmv0,hmk1,hmv1), {hmk0,hmv0,hmk1,hmv1} -> h }
|
||||
|
||||
@ -216,7 +216,7 @@ pub fn main() {
|
||||
// break;
|
||||
}
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 5: { vd[0] -> vd[1], vd[1] -> vd[0] };
|
||||
// does not exercise vd itself
|
||||
@ -232,7 +232,7 @@ pub fn main() {
|
||||
vd[0].descend_into_self(&mut c);
|
||||
assert!(c.saw_prev_marked);
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 6: { vd -> (vd0, vd1), {vd0, vd1} -> vd }
|
||||
let mut vd: VecDeque<VD> = VecDeque::new();
|
||||
@ -247,7 +247,7 @@ pub fn main() {
|
||||
vd[0].descend_into_self(&mut c);
|
||||
assert!(c.saw_prev_marked);
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 7: { vm -> (vm0, vm1), {vm0, vm1} -> vm }
|
||||
let mut vm: HashMap<usize, VM> = HashMap::new();
|
||||
@ -262,7 +262,7 @@ pub fn main() {
|
||||
vm[&0].descend_into_self(&mut c);
|
||||
assert!(c.saw_prev_marked);
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 8: { ll -> (ll0, ll1), {ll0, ll1} -> ll }
|
||||
let mut ll: LinkedList<LL> = LinkedList::new();
|
||||
@ -282,7 +282,7 @@ pub fn main() {
|
||||
// break;
|
||||
}
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 9: { bh -> (bh0, bh1), {bh0, bh1} -> bh }
|
||||
let mut bh: BinaryHeap<BH> = BinaryHeap::new();
|
||||
@ -302,7 +302,7 @@ pub fn main() {
|
||||
// break;
|
||||
}
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 10: { btm -> (btk0, btv1), {bt0, bt1} -> btm }
|
||||
let mut btm: BTreeMap<BTM, BTM> = BTreeMap::new();
|
||||
@ -323,7 +323,7 @@ pub fn main() {
|
||||
// break;
|
||||
}
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 10: { bts -> (bts0, bts1), {bts0, bts1} -> btm }
|
||||
let mut bts: BTreeSet<BTS> = BTreeSet::new();
|
||||
@ -343,7 +343,7 @@ pub fn main() {
|
||||
// break;
|
||||
}
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 11: { rc0 -> (rc1, rc2), rc1 -> (), rc2 -> rc0 }
|
||||
let (rc0, rc1, rc2): (RCRC, RCRC, RCRC);
|
||||
@ -361,7 +361,7 @@ pub fn main() {
|
||||
rc0.descend_into_self(&mut c);
|
||||
assert!(c.saw_prev_marked);
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// We want to take the previous Rc case and generalize it to Arc.
|
||||
//
|
||||
@ -395,7 +395,7 @@ pub fn main() {
|
||||
arc0.descend_into_self(&mut c);
|
||||
assert!(c.saw_prev_marked);
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 13: { arc0 -> (arc1, arc2), arc1 -> (), arc2 -> arc0 }, rwlocks
|
||||
let (arc0, arc1, arc2): (ARCRW, ARCRW, ARCRW);
|
||||
@ -413,7 +413,7 @@ pub fn main() {
|
||||
arc0.descend_into_self(&mut c);
|
||||
assert!(c.saw_prev_marked);
|
||||
|
||||
if PRINT { println!(""); }
|
||||
if PRINT { println!(); }
|
||||
|
||||
// Cycle 14: { arc0 -> (arc1, arc2), arc1 -> (), arc2 -> arc0 }, mutexs
|
||||
let (arc0, arc1, arc2): (ARCM, ARCM, ARCM);
|
||||
|
@ -6,6 +6,6 @@ fn main() {
|
||||
//~| NOTE `&str` is not an iterator
|
||||
//~| HELP the trait `std::iter::Iterator` is not implemented for `&str`
|
||||
//~| NOTE required by `std::iter::IntoIterator::into_iter`
|
||||
println!("");
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
@ -7,3 +7,5 @@ edition = "2018"
|
||||
[dependencies]
|
||||
toml = "0.5"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
reqwest = "0.9"
|
||||
|
@ -1,12 +1,19 @@
|
||||
//! Build a dist manifest, hash and sign everything.
|
||||
//! This gets called by `promote-release`
|
||||
//! (https://github.com/rust-lang/rust-central-station/tree/master/promote-release)
|
||||
//! via `x.py dist hash-and-sign`; the cmdline arguments are set up
|
||||
//! by rustbuild (in `src/bootstrap/dist.rs`).
|
||||
|
||||
use toml;
|
||||
use serde::Serialize;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::io::{self, Read, Write, BufRead, BufReader};
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::collections::HashMap;
|
||||
|
||||
static HOSTS: &[&str] = &[
|
||||
"aarch64-unknown-linux-gnu",
|
||||
@ -146,6 +153,9 @@ static MINGW: &[&str] = &[
|
||||
"x86_64-pc-windows-gnu",
|
||||
];
|
||||
|
||||
static TOOLSTATE: &str =
|
||||
"https://raw.githubusercontent.com/rust-lang-nursery/rust-toolstate/master/history/linux.tsv";
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct Manifest {
|
||||
@ -270,6 +280,7 @@ fn main() {
|
||||
// Do not ask for a passphrase while manually testing
|
||||
let mut passphrase = String::new();
|
||||
if should_sign {
|
||||
// `x.py` passes the passphrase via stdin.
|
||||
t!(io::stdin().read_to_string(&mut passphrase));
|
||||
}
|
||||
|
||||
@ -353,6 +364,7 @@ impl Builder {
|
||||
self.lldb_git_commit_hash = self.git_commit_hash("lldb", "x86_64-unknown-linux-gnu");
|
||||
self.miri_git_commit_hash = self.git_commit_hash("miri", "x86_64-unknown-linux-gnu");
|
||||
|
||||
self.check_toolstate();
|
||||
self.digest_and_sign();
|
||||
let manifest = self.build_manifest();
|
||||
self.write_channel_files(&self.rust_release, &manifest);
|
||||
@ -362,6 +374,37 @@ impl Builder {
|
||||
}
|
||||
}
|
||||
|
||||
/// If a tool does not pass its tests, don't ship it.
|
||||
/// Right now, we do this only for Miri.
|
||||
fn check_toolstate(&mut self) {
|
||||
// Get the toolstate for this rust revision.
|
||||
let rev = self.rust_git_commit_hash.as_ref().expect("failed to determine rust git hash");
|
||||
let toolstates = reqwest::get(TOOLSTATE).expect("failed to get toolstates");
|
||||
let toolstates = BufReader::new(toolstates);
|
||||
let toolstate = toolstates.lines()
|
||||
.find_map(|line| {
|
||||
let line = line.expect("failed to read toolstate lines");
|
||||
let mut pieces = line.splitn(2, '\t');
|
||||
let commit = pieces.next().expect("malformed toolstate line");
|
||||
if commit != rev {
|
||||
// Not the right commit.
|
||||
return None;
|
||||
}
|
||||
// Return the 2nd piece, the JSON.
|
||||
Some(pieces.next().expect("malformed toolstate line").to_owned())
|
||||
})
|
||||
.expect("failed to find toolstate for rust commit");
|
||||
let toolstate: HashMap<String, String> =
|
||||
serde_json::from_str(&toolstate).expect("toolstate is malformed JSON");
|
||||
// Mark some tools as missing based on toolstate.
|
||||
if toolstate.get("miri").map(|s| &*s as &str) != Some("test-pass") {
|
||||
println!("Miri tests are not passing, removing component");
|
||||
self.miri_version = None;
|
||||
self.miri_git_commit_hash = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash all files, compute their signatures, and collect the hashes in `self.digests`.
|
||||
fn digest_and_sign(&mut self) {
|
||||
for file in t!(self.input.read_dir()).map(|e| t!(e).path()) {
|
||||
let filename = file.file_name().unwrap().to_str().unwrap();
|
||||
@ -532,19 +575,20 @@ impl Builder {
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.map(|version| (version, true))
|
||||
.unwrap_or_default();
|
||||
.unwrap_or_default(); // `is_present` defaults to `false` here.
|
||||
|
||||
// miri needs to build std with xargo, which doesn't allow stable/beta:
|
||||
// <https://github.com/japaric/xargo/pull/204#issuecomment-374888868>
|
||||
// Miri is nightly-only; never ship it for other trains.
|
||||
if pkgname == "miri-preview" && self.rust_release != "nightly" {
|
||||
is_present = false; // ignore it
|
||||
is_present = false; // Pretend the component is entirely missing.
|
||||
}
|
||||
|
||||
let targets = targets.iter().map(|name| {
|
||||
if is_present {
|
||||
// The component generally exists, but it might still be missing for this target.
|
||||
let filename = self.filename(pkgname, name);
|
||||
let digest = match self.digests.remove(&filename) {
|
||||
Some(digest) => digest,
|
||||
// This component does not exist for this target -- skip it.
|
||||
None => return (name.to_string(), Target::unavailable()),
|
||||
};
|
||||
let xz_filename = filename.replace(".tar.gz", ".tar.xz");
|
||||
|
@ -253,7 +253,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
||||
if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
|
||||
let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
|
||||
println!("{}", opts.usage(&message));
|
||||
println!("");
|
||||
println!();
|
||||
panic!()
|
||||
}
|
||||
|
||||
@ -265,7 +265,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
||||
if matches.opt_present("h") || matches.opt_present("help") {
|
||||
let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
|
||||
println!("{}", opts.usage(&message));
|
||||
println!("");
|
||||
println!();
|
||||
panic!()
|
||||
}
|
||||
|
||||
|
@ -2593,7 +2593,7 @@ impl<'test> TestCx<'test> {
|
||||
" actual: {}",
|
||||
codegen_units_to_str(&actual_item.codegen_units)
|
||||
);
|
||||
println!("");
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3526,7 +3526,7 @@ impl<'test> TestCx<'test> {
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("");
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
## It is set as callback for `src/ci/docker/x86_64-gnu-tools/repo.sh` by the CI scripts
|
||||
## when a new commit lands on `master` (i.e., after it passed all checks on `auto`).
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
@ -20,21 +22,26 @@ except ImportError:
|
||||
import urllib.request as urllib2
|
||||
|
||||
# List of people to ping when the status of a tool or a book changed.
|
||||
# These should be collaborators of the rust-lang/rust repository (with at least
|
||||
# read privileges on it). CI will fail otherwise.
|
||||
MAINTAINERS = {
|
||||
'miri': '@oli-obk @RalfJung @eddyb',
|
||||
'clippy-driver': '@Manishearth @llogiq @mcarton @oli-obk @phansch @flip1995 @yaahc',
|
||||
'rls': '@Xanewok',
|
||||
'rustfmt': '@topecongiro',
|
||||
'book': '@carols10cents @steveklabnik',
|
||||
'nomicon': '@frewsxcv @Gankro',
|
||||
'reference': '@steveklabnik @Havvy @matthewjasper @ehuss',
|
||||
'rust-by-example': '@steveklabnik @marioidival @projektir',
|
||||
'embedded-book': (
|
||||
'@adamgreig @andre-richter @jamesmunns @korken89 '
|
||||
'@ryankurte @thejpster @therealprof'
|
||||
),
|
||||
'edition-guide': '@ehuss @Centril @steveklabnik',
|
||||
'rustc-guide': '@mark-i-m @spastorino @amanjeev'
|
||||
'miri': {'oli-obk', 'RalfJung', 'eddyb'},
|
||||
'clippy-driver': {
|
||||
'Manishearth', 'llogiq', 'mcarton', 'oli-obk', 'phansch', 'flip1995',
|
||||
'yaahc',
|
||||
},
|
||||
'rls': {'Xanewok'},
|
||||
'rustfmt': {'topecongiro'},
|
||||
'book': {'carols10cents', 'steveklabnik'},
|
||||
'nomicon': {'frewsxcv', 'Gankra'},
|
||||
'reference': {'steveklabnik', 'Havvy', 'matthewjasper', 'ehuss'},
|
||||
'rust-by-example': {'steveklabnik', 'marioidival'},
|
||||
'embedded-book': {
|
||||
'adamgreig', 'andre-richter', 'jamesmunns', 'korken89',
|
||||
'ryankurte', 'thejpster', 'therealprof',
|
||||
},
|
||||
'edition-guide': {'ehuss', 'Centril', 'steveklabnik'},
|
||||
'rustc-guide': {'mark-i-m', 'spastorino', 'amanjeev'},
|
||||
}
|
||||
|
||||
REPOS = {
|
||||
@ -52,6 +59,50 @@ REPOS = {
|
||||
}
|
||||
|
||||
|
||||
def validate_maintainers(repo, github_token):
|
||||
'''Ensure all maintainers are assignable on a GitHub repo'''
|
||||
next_link_re = re.compile(r'<([^>]+)>; rel="next"')
|
||||
|
||||
# Load the list of assignable people in the GitHub repo
|
||||
assignable = []
|
||||
url = 'https://api.github.com/repos/%s/collaborators?per_page=100' % repo
|
||||
while url is not None:
|
||||
response = urllib2.urlopen(urllib2.Request(url, headers={
|
||||
'Authorization': 'token ' + github_token,
|
||||
# Properly load nested teams.
|
||||
'Accept': 'application/vnd.github.hellcat-preview+json',
|
||||
}))
|
||||
assignable.extend(user['login'] for user in json.load(response))
|
||||
# Load the next page if available
|
||||
url = None
|
||||
link_header = response.headers.get('Link')
|
||||
if link_header:
|
||||
matches = next_link_re.match(link_header)
|
||||
if matches is not None:
|
||||
url = matches.group(1)
|
||||
|
||||
errors = False
|
||||
for tool, maintainers in MAINTAINERS.items():
|
||||
for maintainer in maintainers:
|
||||
if maintainer not in assignable:
|
||||
errors = True
|
||||
print(
|
||||
"error: %s maintainer @%s is not assignable in the %s repo"
|
||||
% (tool, maintainer, repo),
|
||||
)
|
||||
|
||||
if errors:
|
||||
print()
|
||||
print(" To be assignable, a person needs to be explicitly listed as a")
|
||||
print(" collaborator in the repository settings. The simple way to")
|
||||
print(" fix this is to ask someone with 'admin' privileges on the repo")
|
||||
print(" to add the person or whole team as a collaborator with 'read'")
|
||||
print(" privileges. Those privileges don't grant any extra permissions")
|
||||
print(" so it's safe to apply them.")
|
||||
print()
|
||||
print("The build will fail due to this.")
|
||||
exit(1)
|
||||
|
||||
def read_current_status(current_commit, path):
|
||||
'''Reads build status of `current_commit` from content of `history/*.tsv`
|
||||
'''
|
||||
@ -73,13 +124,12 @@ def maybe_delink(message):
|
||||
def issue(
|
||||
tool,
|
||||
status,
|
||||
maintainers,
|
||||
assignees,
|
||||
relevant_pr_number,
|
||||
relevant_pr_user,
|
||||
pr_reviewer,
|
||||
):
|
||||
# Open an issue about the toolstate failure.
|
||||
assignees = [x.strip() for x in maintainers.split('@') if x != '']
|
||||
if status == 'test-fail':
|
||||
status_description = 'has failing tests'
|
||||
else:
|
||||
@ -100,7 +150,7 @@ def issue(
|
||||
REPOS.get(tool), relevant_pr_user, pr_reviewer
|
||||
)),
|
||||
'title': '`{}` no longer builds after {}'.format(tool, relevant_pr_number),
|
||||
'assignees': assignees,
|
||||
'assignees': list(assignees),
|
||||
'labels': ['T-compiler', 'I-nominated'],
|
||||
})
|
||||
print("Creating issue:\n{}".format(request))
|
||||
@ -150,18 +200,19 @@ def update_latest(
|
||||
old = status[os]
|
||||
new = s.get(tool, old)
|
||||
status[os] = new
|
||||
maintainers = ' '.join('@'+name for name in MAINTAINERS[tool])
|
||||
if new > old: # comparing the strings, but they are ordered appropriately!
|
||||
# things got fixed or at least the status quo improved
|
||||
changed = True
|
||||
message += '🎉 {} on {}: {} → {} (cc {}, @rust-lang/infra).\n' \
|
||||
.format(tool, os, old, new, MAINTAINERS.get(tool))
|
||||
.format(tool, os, old, new, maintainers)
|
||||
elif new < old:
|
||||
# tests or builds are failing and were not failing before
|
||||
changed = True
|
||||
title = '💔 {} on {}: {} → {}' \
|
||||
.format(tool, os, old, new)
|
||||
message += '{} (cc {}, @rust-lang/infra).\n' \
|
||||
.format(title, MAINTAINERS.get(tool))
|
||||
.format(title, maintainers)
|
||||
# Most tools only create issues for build failures.
|
||||
# Other failures can be spurious.
|
||||
if new == 'build-fail' or (tool == 'miri' and new == 'test-fail'):
|
||||
@ -200,6 +251,16 @@ def update_latest(
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
repo = os.environ.get('TOOLSTATE_VALIDATE_MAINTAINERS_REPO')
|
||||
if repo:
|
||||
github_token = os.environ.get('TOOLSTATE_REPO_ACCESS_TOKEN')
|
||||
if github_token:
|
||||
validate_maintainers(repo, github_token)
|
||||
else:
|
||||
print('skipping toolstate maintainers validation since no GitHub token is present')
|
||||
# When validating maintainers don't run the full script.
|
||||
exit(0)
|
||||
|
||||
cur_commit = sys.argv[1]
|
||||
cur_datetime = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
cur_commit_msg = sys.argv[2]
|
||||
|
Loading…
Reference in New Issue
Block a user