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:
bors 2019-09-16 15:35:48 +00:00
commit a44881d892
39 changed files with 478 additions and 247 deletions

View File

@ -201,7 +201,9 @@ dependencies = [
name = "build-manifest" name = "build-manifest"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"reqwest",
"serde", "serde",
"serde_json",
"toml", "toml",
] ]

View File

@ -2000,6 +2000,8 @@ impl Step for HashSign {
} }
fn run(self, builder: &Builder<'_>) { 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); let mut cmd = builder.tool_cmd(Tool::BuildManifest);
if builder.config.dry_run { if builder.config.dry_run {
return; return;
@ -2010,10 +2012,14 @@ impl Step for HashSign {
let addr = builder.config.dist_upload_addr.as_ref().unwrap_or_else(|| { 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") 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(|| { let pass = if env::var("BUILD_MANIFEST_DISABLE_SIGNING").is_err() {
panic!("\n\nfailed to specify `dist.gpg-password-file` 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)); });
t!(fs::read_to_string(&file))
} else {
String::new()
};
let today = output(Command::new("date").arg("+%Y-%m-%d")); let today = output(Command::new("date").arg("+%Y-%m-%d"));

View File

@ -147,8 +147,15 @@ steps:
git clone --depth=1 https://github.com/rust-lang-nursery/rust-toolstate.git git clone --depth=1 https://github.com/rust-lang-nursery/rust-toolstate.git
cd rust-toolstate cd rust-toolstate
python2.7 "$BUILD_SOURCESDIRECTORY/src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" "$(git log --format=%s -n1 HEAD)" "" "" 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 .. cd ..
rm -rf rust-toolstate 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')) condition: and(succeeded(), not(variables.SKIP_JOB), eq(variables['IMAGE'], 'mingw-check'))
displayName: Verify the publish_toolstate script works displayName: Verify the publish_toolstate script works

View File

@ -117,7 +117,7 @@
#![feature(allocator_internals)] #![feature(allocator_internals)]
#![feature(on_unimplemented)] #![feature(on_unimplemented)]
#![feature(rustc_const_unstable)] #![feature(rustc_const_unstable)]
#![feature(const_vec_new)] #![cfg_attr(bootstrap, feature(const_vec_new))]
#![feature(slice_partition_dedup)] #![feature(slice_partition_dedup)]
#![feature(maybe_uninit_extra, maybe_uninit_slice)] #![feature(maybe_uninit_extra, maybe_uninit_slice)]
#![feature(alloc_layout_extra)] #![feature(alloc_layout_extra)]

View File

@ -113,13 +113,38 @@ impl<T, A: Alloc> RawVec<T, A> {
} }
impl<T> RawVec<T, Global> { 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) /// Creates the biggest possible `RawVec` (on the system heap)
/// without allocating. If `T` has positive size, then this makes a /// 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 `0`. If `T` is zero-sized, then it makes a
/// `RawVec` with capacity `usize::MAX`. Useful for implementing /// `RawVec` with capacity `usize::MAX`. Useful for implementing
/// delayed allocation. /// delayed allocation.
pub const fn new() -> Self { 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 /// Creates a `RawVec` (on the system heap) with exactly the

View File

@ -369,7 +369,7 @@ impl String {
/// ``` /// ```
#[inline] #[inline]
#[stable(feature = "rust1", since = "1.0.0")] #[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 { pub const fn new() -> String {
String { vec: Vec::new() } String { vec: Vec::new() }
} }

View File

@ -314,10 +314,10 @@ impl<T> Vec<T> {
/// ``` /// ```
#[inline] #[inline]
#[stable(feature = "rust1", since = "1.0.0")] #[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> { pub const fn new() -> Vec<T> {
Vec { Vec {
buf: RawVec::new(), buf: RawVec::NEW,
len: 0, len: 0,
} }
} }

View File

@ -7,6 +7,7 @@ use crate::session::{early_error, early_warn, Session};
use crate::session::search_paths::SearchPath; use crate::session::search_paths::SearchPath;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel};
use rustc_target::spec::{Target, TargetTriple}; 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::parse::token;
use syntax::symbol::{sym, Symbol}; use syntax::symbol::{sym, Symbol};
use syntax::feature_gate::UnstableFeatures; use syntax::feature_gate::UnstableFeatures;
use syntax::source_map::SourceMap;
use errors::emitter::HumanReadableErrorType; use errors::emitter::HumanReadableErrorType;
use errors::{ColorConfig, FatalError, Handler}; use errors::{ColorConfig, FatalError, Handler};
@ -1850,11 +1852,20 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
opts 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`. // Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.
pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> { pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> {
syntax::with_default_globals(move || { syntax::with_default_globals(move || {
let cfg = cfgspecs.into_iter().map(|s| { 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 filename = FileName::cfg_spec_source_code(&s);
let mut parser = new_parser_from_source_str(&sess, filename, s.to_string()); let mut parser = new_parser_from_source_str(&sess, filename, s.to_string());

View File

@ -4,6 +4,7 @@ use build_helper::sanitizer_lib_boilerplate;
use cmake::Config; use cmake::Config;
fn main() { fn main() {
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) { if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
return; return;
} }

View File

@ -226,21 +226,21 @@ impl CodegenBackend for LlvmCodegenBackend {
for &(name, _) in back::write::RELOC_MODEL_ARGS.iter() { for &(name, _) in back::write::RELOC_MODEL_ARGS.iter() {
println!(" {}", name); println!(" {}", name);
} }
println!(""); println!();
} }
PrintRequest::CodeModels => { PrintRequest::CodeModels => {
println!("Available code models:"); println!("Available code models:");
for &(name, _) in back::write::CODE_GEN_MODEL_ARGS.iter(){ for &(name, _) in back::write::CODE_GEN_MODEL_ARGS.iter(){
println!(" {}", name); println!(" {}", name);
} }
println!(""); println!();
} }
PrintRequest::TlsModels => { PrintRequest::TlsModels => {
println!("Available TLS models:"); println!("Available TLS models:");
for &(name, _) in back::write::TLS_MODEL_ARGS.iter(){ for &(name, _) in back::write::TLS_MODEL_ARGS.iter(){
println!(" {}", name); println!(" {}", name);
} }
println!(""); println!();
} }
req => llvm_util::print(req, sess), req => llvm_util::print(req, sess),
} }

View File

@ -134,8 +134,11 @@ pub struct TimePassesCallbacks {
impl Callbacks for TimePassesCallbacks { impl Callbacks for TimePassesCallbacks {
fn config(&mut self, config: &mut interface::Config) { 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 = 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);
} }
} }

View File

@ -4,6 +4,7 @@ use build_helper::sanitizer_lib_boilerplate;
use cmake::Config; use cmake::Config;
fn main() { fn main() {
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) { if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
return; return;
} }

View File

@ -134,9 +134,8 @@ fn eval_body_using_ecx<'mir, 'tcx>(
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
cid: GlobalId<'tcx>, cid: GlobalId<'tcx>,
body: &'mir mir::Body<'tcx>, body: &'mir mir::Body<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> InterpResult<'tcx, MPlaceTy<'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 tcx = ecx.tcx.tcx;
let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?; let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?;
assert!(!layout.is_unsized()); assert!(!layout.is_unsized());
@ -162,7 +161,6 @@ fn eval_body_using_ecx<'mir, 'tcx>(
ecx, ecx,
cid.instance.def_id(), cid.instance.def_id(),
ret, ret,
param_env,
)?; )?;
debug!("eval_body_using_ecx done: {:?}", *ret); 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); let res = ecx.load_mir(cid.instance.def, cid.promoted);
res.and_then( 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| { ).and_then(|place| {
Ok(RawConst { Ok(RawConst {
alloc_id: place.ptr.assert_ptr().alloc_id, alloc_id: place.ptr.assert_ptr().alloc_id,

View File

@ -3,7 +3,7 @@
//! After a const evaluation has computed a value, before we destroy the const evaluator's session //! 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. //! 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::mir::interpret::{InterpResult, ErrorHandled};
use rustc::hir; use rustc::hir;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
@ -11,32 +11,29 @@ use super::validity::RefTracking;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use syntax::ast::Mutability; use syntax::ast::Mutability;
use syntax_pos::Span;
use super::{ use super::{
ValueVisitor, MemoryKind, Pointer, AllocId, MPlaceTy, Scalar, ValueVisitor, MemoryKind, AllocId, MPlaceTy, Scalar,
}; };
use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext}; use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};
struct InternVisitor<'rt, 'mir, 'tcx> { struct InternVisitor<'rt, 'mir, 'tcx> {
/// previously encountered safe references /// The ectx from which we intern.
ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>, 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 /// 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. /// for sanity assertions that will ICE when `const_qualif` screws up.
mode: InternMode, mode: InternMode,
/// This field stores the mutability of the value *currently* being checked. /// This field stores the mutability of the value *currently* being checked.
/// It is set to mutable when an `UnsafeCell` is encountered /// When encountering a mutable reference, we determine the pointee mutability
/// When recursing across a reference, we don't recurse but store the /// taking into account the mutability of the context: `& &mut i32` is entirely immutable,
/// value to be checked in `ref_tracking` together with the mutability at which we are checking /// despite the nested mutable reference!
/// the value. /// The field gets updated when an `UnsafeCell` is encountered.
/// When encountering an immutable reference, we treat everything as immutable that is behind
/// it.
mutability: Mutability, 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)] #[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` /// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
/// that will actually be treated as mutable. /// that will actually be treated as mutable.
Static, 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, ConstBase,
/// `UnsafeCell` ICEs /// `UnsafeCell` ICEs.
Const, Const,
} }
@ -55,48 +53,100 @@ enum InternMode {
/// into the memory of other constants or statics /// into the memory of other constants or statics
struct IsStaticOrFn; 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> { impl<'rt, 'mir, 'tcx> InternVisitor<'rt, 'mir, 'tcx> {
/// Intern an allocation without looking at its children
fn intern_shallow( fn intern_shallow(
&mut self, &mut self,
ptr: Pointer, alloc_id: AllocId,
mutability: Mutability, mutability: Mutability,
ty: Option<Ty<'tcx>>,
) -> InterpResult<'tcx, Option<IsStaticOrFn>> { ) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
trace!( intern_shallow(
"InternVisitor::intern {:?} with {:?}", self.ecx,
ptr, mutability, self.leftover_allocations,
); self.mode,
// remove allocation alloc_id,
let tcx = self.ecx.tcx; mutability,
let memory = self.ecx.memory_mut(); ty,
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)
} }
} }
@ -119,14 +169,16 @@ for
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
if let Some(def) = mplace.layout.ty.ty_adt_def() { if let Some(def) = mplace.layout.ty.ty_adt_def() {
if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() { 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); let old = std::mem::replace(&mut self.mutability, Mutability::Mutable);
assert_ne!( assert_ne!(
self.mode, InternMode::Const, self.mode, InternMode::Const,
"UnsafeCells are not allowed behind references in constants. This should have \ "UnsafeCells are not allowed behind references in constants. This should have \
been prevented statically by const qualification. If this were allowed one \ 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 \ would be able to change a constant at one use site and other use sites could \
arbitrarily decide to change, too.", observe that mutation.",
); );
let walked = self.walk_aggregate(mplace, fields); let walked = self.walk_aggregate(mplace, fields);
self.mutability = old; self.mutability = old;
@ -145,12 +197,13 @@ for
// Handle trait object vtables // Handle trait object vtables
if let Ok(meta) = value.to_meta() { if let Ok(meta) = value.to_meta() {
if let ty::Dynamic(..) = 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() { if let Ok(vtable) = meta.unwrap().to_ptr() {
// explitly choose `Immutable` here, since vtables are immutable, even // explitly choose `Immutable` here, since vtables are immutable, even
// if the reference of the fat pointer is mutable // 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) => { (InternMode::Const, hir::Mutability::MutMutable) => {
match referenced_ty.sty { match referenced_ty.sty {
ty::Array(_, n) 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(_) ty::Slice(_)
if value.to_meta().unwrap().unwrap().to_usize(self.ecx)? == 0 => {} if value.to_meta().unwrap().unwrap().to_usize(self.ecx)? == 0 => {}
_ => bug!("const qualif failed to prevent mutable references"), _ => bug!("const qualif failed to prevent mutable references"),
@ -195,21 +248,13 @@ for
(Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable, (Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable,
_ => Mutability::Immutable, _ => 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 // Recursing behind references changes the intern mode for constants in order to
// cause assertions to trigger if we encounter any `UnsafeCell`s. // cause assertions to trigger if we encounter any `UnsafeCell`s.
let mode = match self.mode { let mode = match self.mode {
InternMode::ConstBase => InternMode::Const, InternMode::ConstBase => InternMode::Const,
other => other, 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 // No need to recurse, these are interned already and statics may have
// cycles, so we don't want to recurse there // cycles, so we don't want to recurse there
Some(IsStaticOrFn) => {}, 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( pub fn intern_const_alloc_recursive(
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
def_id: DefId, def_id: DefId,
ret: MPlaceTy<'tcx>, 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> { ) -> InterpResult<'tcx> {
let tcx = ecx.tcx; let tcx = ecx.tcx;
// this `mutability` is the mutability of the place, ignoring the type // 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), 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 // `static mut` doesn't care about interior mutability, it's mutable anyway
Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static), 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 // Type based interning.
let mut ref_tracking = RefTracking::new((ret, mutability, base_intern_mode)); // `ref_tracking` tracks typed references we have seen and still need to crawl for
let leftover_relocations = &mut FxHashSet::default(); // more typed information inside them.
// `leftover_allocations` collects *all* allocations we see, because some might not
// This mutability is the combination of the place mutability and the type mutability. If either // be available in a typed way. They get interned at the end.
// is mutable, `alloc_mutability` is mutable. This exists because the entire allocation needs let mut ref_tracking = RefTracking::new((ret, base_mutability, base_intern_mode));
// to be mutable if it contains an `UnsafeCell` anywhere. The other `mutability` exists so that let leftover_allocations = &mut FxHashSet::default();
// 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,
);
// start with the outermost allocation // start with the outermost allocation
InternVisitor { intern_shallow(
ref_tracking: &mut ref_tracking,
ecx, ecx,
mode: base_intern_mode, leftover_allocations,
leftover_relocations, base_intern_mode,
param_env, ret.ptr.to_ptr()?.alloc_id,
mutability, base_mutability,
}.intern_shallow(ret.ptr.to_ptr()?, alloc_mutability)?; Some(ret.layout.ty)
)?;
while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() { while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
let interned = InternVisitor { let interned = InternVisitor {
ref_tracking: &mut ref_tracking, ref_tracking: &mut ref_tracking,
ecx, ecx,
mode, mode,
leftover_relocations, leftover_allocations,
param_env,
mutability, mutability,
}.visit_value(mplace); }.visit_value(mplace);
if let Err(error) = interned { 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 // 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 // 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() { while let Some(alloc_id) = todo.pop() {
if let Some((_, alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) { if let Some((_, mut 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. // We can't call the `intern_shallow` method here, as its logic is tailored to safe
// So we hand-roll the interning logic here again // 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); let alloc = tcx.intern_const_alloc(alloc);
tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc); tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
for &(_, ((), reloc)) in alloc.relocations().iter() { for &(_, ((), reloc)) in alloc.relocations().iter() {
if leftover_relocations.insert(reloc) { if leftover_allocations.insert(reloc) {
todo.push(reloc); todo.push(reloc);
} }
} }

View File

@ -4,6 +4,7 @@ use build_helper::sanitizer_lib_boilerplate;
use cmake::Config; use cmake::Config;
fn main() { fn main() {
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) { if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
return; return;
} }

View File

@ -4,6 +4,7 @@ use build_helper::sanitizer_lib_boilerplate;
use cmake::Config; use cmake::Config;
fn main() { fn main() {
println!("cargo:rerun-if-env-changed=RUSTC_BUILD_SANITIZERS");
if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) { if env::var("RUSTC_BUILD_SANITIZERS") != Ok("1".to_string()) {
return; return;
} }

View File

@ -25,17 +25,11 @@ profiler_builtins = { path = "../libprofiler_builtins", optional = true }
unwind = { path = "../libunwind" } unwind = { path = "../libunwind" }
hashbrown = { version = "0.5.0", features = ['rustc-dep-of-std'] } hashbrown = { version = "0.5.0", features = ['rustc-dep-of-std'] }
[dependencies.backtrace] [dependencies.backtrace_rs]
package = "backtrace"
version = "0.3.37" version = "0.3.37"
default-features = false # don't use coresymbolication on OSX default-features = false # without the libstd `backtrace` feature, stub out everything
features = [ features = [ "rustc-dep-of-std" ] # enable build support for integrating into libstd
"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
[dev-dependencies] [dev-dependencies]
rand = "0.7" rand = "0.7"
@ -65,6 +59,13 @@ cc = "1.0"
[features] [features]
default = ["std_detect_file_io", "std_detect_dlsym_getauxval"] 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"] panic-unwind = ["panic_unwind"]
profiler = ["profiler_builtins"] profiler = ["profiler_builtins"]
compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-c = ["alloc/compiler-builtins-c"]

View File

@ -97,6 +97,7 @@ use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use crate::sync::Mutex; use crate::sync::Mutex;
use crate::sys_common::backtrace::{output_filename, lock}; use crate::sys_common::backtrace::{output_filename, lock};
use crate::vec::Vec; use crate::vec::Vec;
use backtrace_rs as backtrace;
use backtrace::BytesOrWideString; use backtrace::BytesOrWideString;
/// A captured OS thread stack backtrace. /// A captured OS thread stack backtrace.

View File

@ -17,8 +17,7 @@ use crate::ptr;
use crate::raw; use crate::raw;
use crate::sys::stdio::panic_output; use crate::sys::stdio::panic_output;
use crate::sys_common::rwlock::RWLock; use crate::sys_common::rwlock::RWLock;
use crate::sys_common::thread_info; use crate::sys_common::{thread_info, util, backtrace};
use crate::sys_common::util;
use crate::thread; use crate::thread;
#[cfg(not(test))] #[cfg(not(test))]
@ -157,20 +156,18 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
} }
fn default_hook(info: &PanicInfo<'_>) { 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 // If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled. // for this panic. Otherwise only print it if logging is enabled.
#[cfg(feature = "backtrace")] let log_backtrace = if cfg!(feature = "backtrace") {
let log_backtrace = {
let panics = update_panic_count(0); let panics = update_panic_count(0);
if panics >= 2 { if panics >= 2 {
Some(backtrace::PrintFmt::Full) Some(backtrace_rs::PrintFmt::Full)
} else { } else {
backtrace_mod::log_enabled() backtrace::log_enabled()
} }
} else {
None
}; };
// The current implementation always returns `Some`. // The current implementation always returns `Some`.
@ -190,14 +187,13 @@ fn default_hook(info: &PanicInfo<'_>) {
let _ = writeln!(err, "thread '{}' panicked at '{}', {}", let _ = writeln!(err, "thread '{}' panicked at '{}', {}",
name, msg, location); name, msg, location);
#[cfg(feature = "backtrace")] if cfg!(feature = "backtrace") {
{
use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::atomic::{AtomicBool, Ordering};
static FIRST_PANIC: AtomicBool = AtomicBool::new(true); static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
if let Some(format) = log_backtrace { 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) { } else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \ let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \
environment variable to display a backtrace."); environment variable to display a backtrace.");

View File

@ -422,7 +422,7 @@ impl fmt::Debug for ChildStderr {
/// // Execute `ls` in the current directory of the program. /// // Execute `ls` in the current directory of the program.
/// list_dir.status().expect("process failed to execute"); /// list_dir.status().expect("process failed to execute");
/// ///
/// println!(""); /// println!();
/// ///
/// // Change `ls` to execute in the root directory. /// // Change `ls` to execute in the root directory.
/// list_dir.current_dir("/"); /// list_dir.current_dir("/");

View File

@ -7,10 +7,9 @@ use crate::io;
use crate::borrow::Cow; use crate::borrow::Cow;
use crate::io::prelude::*; use crate::io::prelude::*;
use crate::path::{self, Path, PathBuf}; use crate::path::{self, Path, PathBuf};
use crate::sync::atomic::{self, Ordering};
use crate::sys::mutex::Mutex; use crate::sys::mutex::Mutex;
use backtrace::{BacktraceFmt, BytesOrWideString, PrintFmt}; use backtrace_rs::{BacktraceFmt, BytesOrWideString, PrintFmt};
/// Max number of frames to print. /// Max number of frames to print.
const MAX_NB_FRAMES: usize = 100; 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()?; bt_fmt.add_context()?;
let mut idx = 0; let mut idx = 0;
let mut res = Ok(()); let mut res = Ok(());
backtrace::trace_unsynchronized(|frame| { backtrace_rs::trace_unsynchronized(|frame| {
if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
return false; return false;
} }
let mut hit = false; let mut hit = false;
let mut stop = false; let mut stop = false;
backtrace::resolve_frame_unsynchronized(frame, |symbol| { backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
hit = true; hit = true;
if print_fmt == PrintFmt::Short { if print_fmt == PrintFmt::Short {
if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { 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 // 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. // whether the magical environment variable is present to see if it's turned on.
pub fn log_enabled() -> Option<PrintFmt> { pub fn log_enabled() -> Option<PrintFmt> {
use crate::sync::atomic::{self, Ordering};
// Setting environment variables for Fuchsia components isn't a standard // Setting environment variables for Fuchsia components isn't a standard
// or easily supported workflow. For now, always display backtraces. // or easily supported workflow. For now, always display backtraces.
if cfg!(target_os = "fuchsia") { if cfg!(target_os = "fuchsia") {

View File

@ -41,7 +41,6 @@ macro_rules! rtunwrap {
pub mod alloc; pub mod alloc;
pub mod at_exit_imp; pub mod at_exit_imp;
#[cfg(feature = "backtrace")]
pub mod backtrace; pub mod backtrace;
pub mod condvar; pub mod condvar;
pub mod io; pub mod io;

View File

@ -877,9 +877,9 @@ fn check_matcher_core(
// Now `last` holds the complete set of NT tokens that could // Now `last` holds the complete set of NT tokens that could
// end the sequence before SUFFIX. Check that every one works with `suffix`. // end the sequence before SUFFIX. Check that every one works with `suffix`.
'each_last: for token in &last.tokens { '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 { 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) => { IsInFollow::Invalid(msg, help) => {
sess.span_diagnostic sess.span_diagnostic
.struct_span_err(next_token.span(), &msg) .struct_span_err(next_token.span(), &msg)
@ -948,7 +948,7 @@ fn check_matcher_core(
fn token_can_be_followed_by_any(tok: &quoted::TokenTree) -> bool { fn token_can_be_followed_by_any(tok: &quoted::TokenTree) -> bool {
if let quoted::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok { 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 { } else {
// (Non NT's can always be followed by anthing in matchers.) // (Non NT's can always be followed by anthing in matchers.)
true true
@ -963,15 +963,15 @@ fn token_can_be_followed_by_any(tok: &quoted::TokenTree) -> bool {
/// specifier which consumes at most one token tree can be followed by /// specifier which consumes at most one token tree can be followed by
/// a fragment specifier (indeed, these fragments can be followed by /// a fragment specifier (indeed, these fragments can be followed by
/// ANYTHING without fear of future compatibility hazards). /// 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 { match frag {
"item" | // always terminated by `}` or `;` sym::item | // always terminated by `}` or `;`
"block" | // exactly one token tree sym::block | // exactly one token tree
"ident" | // exactly one token tree sym::ident | // exactly one token tree
"literal" | // exactly one token tree sym::literal | // exactly one token tree
"meta" | // exactly one token tree sym::meta | // exactly one token tree
"lifetime" | // exactly one token tree sym::lifetime | // exactly one token tree
"tt" => // exactly one token tree sym::tt => // exactly one token tree
true, true,
_ => _ =>
@ -993,7 +993,7 @@ enum IsInFollow {
/// break macros that were relying on that binary operator as a /// break macros that were relying on that binary operator as a
/// separator. /// separator.
// when changing this do not forget to update doc/book/macros.md! // when changing this do not forget to update doc/book/macros.md!
fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> IsInFollow { fn is_in_follow(tok: &quoted::TokenTree, frag: Symbol) -> IsInFollow {
use quoted::TokenTree; use quoted::TokenTree;
if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok { if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok {
@ -1002,17 +1002,17 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> IsInFollow {
IsInFollow::Yes IsInFollow::Yes
} else { } else {
match frag { match frag {
"item" => { sym::item => {
// since items *must* be followed by either a `;` or a `}`, we can // since items *must* be followed by either a `;` or a `}`, we can
// accept anything after them // accept anything after them
IsInFollow::Yes IsInFollow::Yes
} }
"block" => { sym::block => {
// anything can follow block, the braces provide an easy boundary to // anything can follow block, the braces provide an easy boundary to
// maintain // maintain
IsInFollow::Yes IsInFollow::Yes
} }
"stmt" | "expr" => { sym::stmt | sym::expr => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"]; const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"];
match tok { match tok {
TokenTree::Token(token) => match token.kind { TokenTree::Token(token) => match token.kind {
@ -1022,7 +1022,7 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> IsInFollow {
_ => IsInFollow::No(TOKENS), _ => IsInFollow::No(TOKENS),
} }
} }
"pat" => { sym::pat => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
match tok { match tok {
TokenTree::Token(token) => match token.kind { TokenTree::Token(token) => match token.kind {
@ -1033,7 +1033,7 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> IsInFollow {
_ => IsInFollow::No(TOKENS), _ => IsInFollow::No(TOKENS),
} }
} }
"path" | "ty" => { sym::path | sym::ty => {
const TOKENS: &[&str] = &[ const TOKENS: &[&str] = &[
"`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`", "`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`",
"`where`", "`where`",
@ -1061,20 +1061,20 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> IsInFollow {
_ => IsInFollow::No(TOKENS), _ => IsInFollow::No(TOKENS),
} }
} }
"ident" | "lifetime" => { sym::ident | sym::lifetime => {
// being a single token, idents and lifetimes are harmless // being a single token, idents and lifetimes are harmless
IsInFollow::Yes IsInFollow::Yes
} }
"literal" => { sym::literal => {
// literals may be of a single token, or two tokens (negative numbers) // literals may be of a single token, or two tokens (negative numbers)
IsInFollow::Yes IsInFollow::Yes
} }
"meta" | "tt" => { sym::meta | sym::tt => {
// being either a single token or a delimited sequence, tt is // being either a single token or a delimited sequence, tt is
// harmless // harmless
IsInFollow::Yes IsInFollow::Yes
} }
"vis" => { sym::vis => {
// Explicitly disallow `priv`, on the off chance it comes back. // Explicitly disallow `priv`, on the off chance it comes back.
const TOKENS: &[&str] = &["`,`", "an ident", "a type"]; const TOKENS: &[&str] = &["`,`", "an ident", "a type"];
match tok { match tok {
@ -1099,7 +1099,7 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> IsInFollow {
_ => IsInFollow::No(TOKENS), _ => IsInFollow::No(TOKENS),
} }
} }
"" => IsInFollow::Yes, // kw::Invalid kw::Invalid => IsInFollow::Yes,
_ => IsInFollow::Invalid( _ => IsInFollow::Invalid(
format!("invalid fragment specifier `{}`", frag), format!("invalid fragment specifier `{}`", frag),
VALID_FRAGMENT_NAMES_MSG, VALID_FRAGMENT_NAMES_MSG,

View File

@ -242,12 +242,12 @@ fn non_immediate_args(a: BigStruct, b: BigStruct) {
fn binding(a: i64, b: u64, c: f64) { fn binding(a: i64, b: u64, c: f64) {
let x = 0; // #break let x = 0; // #break
println!("") println!()
} }
fn assignment(mut a: u64, b: u64, c: f64) { fn assignment(mut a: u64, b: u64, c: f64) {
a = b; // #break a = b; // #break
println!("") println!()
} }
fn function_call(x: u64, y: u64, z: f64) { fn function_call(x: u64, y: u64, z: f64) {

View File

@ -1,15 +1,11 @@
// run-pass // check-pass
#![allow(dead_code)]
// Test several functions can be used for constants // Test several functions can be used for constants
// 1. Vec::new() // 1. Vec::new()
// 2. String::new() // 2. String::new()
#![feature(const_vec_new)]
#![feature(const_string_new)]
const MY_VEC: Vec<usize> = Vec::new(); const MY_VEC: Vec<usize> = Vec::new();
const MY_STRING: String = String::new(); const MY_STRING: String = String::new();
pub fn main() {} fn main() {}

View File

@ -0,0 +1,3 @@
// compile-flags: --cfg a{
// error-pattern: invalid `--cfg` argument: `a{` (expected `key` or `key="value"`)
fn main() {}

View File

@ -0,0 +1,2 @@
error: invalid `--cfg` argument: `a{` (expected `key` or `key="value"`)

View File

@ -14,8 +14,9 @@ trait Bar<T, U: Foo<T>> {
impl Foo<u32> for () { impl Foo<u32> for () {
const X: u32 = 42; const X: u32 = 42;
} }
impl Foo<Vec<u32>> for String { 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 () {} impl Bar<u32, ()> for () {}

View File

@ -4,13 +4,5 @@ error[E0493]: destructors cannot be evaluated at compile-time
LL | const F: u32 = (U::X, 42).1; LL | const F: u32 = (U::X, 42).1;
| ^^^^^^^^^^ constants cannot evaluate destructors | ^^^^^^^^^^ constants cannot evaluate destructors
error: `std::vec::Vec::<T>::new` is not yet stable as a const fn error: aborting due to previous error
--> $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

View 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() {}

View 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

View File

@ -6,7 +6,7 @@ LL | *MUH.x.get() = 99;
thread 'rustc' panicked at 'assertion failed: `(left != right)` thread 'rustc' panicked at 'assertion failed: `(left != right)`
left: `Const`, 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. note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: internal compiler error: unexpected panic error: internal compiler error: unexpected panic

View File

@ -143,7 +143,7 @@ pub fn main() {
v[0].descend_into_self(&mut c); v[0].descend_into_self(&mut c);
assert!(!c.saw_prev_marked); // <-- different from below, b/c acyclic above 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] }; // Cycle 1: { v[0] -> v[1], v[1] -> v[0] };
// does not exercise `v` itself // does not exercise `v` itself
@ -158,7 +158,7 @@ pub fn main() {
v[0].descend_into_self(&mut c); v[0].descend_into_self(&mut c);
assert!(c.saw_prev_marked); assert!(c.saw_prev_marked);
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 2: { v[0] -> v, v[1] -> v } // Cycle 2: { v[0] -> v, v[1] -> v }
let v: V = Named::new("v"); let v: V = Named::new("v");
@ -171,7 +171,7 @@ pub fn main() {
v.descend_into_self(&mut c); v.descend_into_self(&mut c);
assert!(c.saw_prev_marked); assert!(c.saw_prev_marked);
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 3: { hk0 -> hv0, hv0 -> hk0, hk1 -> hv1, hv1 -> hk1 }; // Cycle 3: { hk0 -> hv0, hv0 -> hk0, hk1 -> hv1, hv1 -> hk1 };
// does not exercise `h` itself // does not exercise `h` itself
@ -193,7 +193,7 @@ pub fn main() {
assert!(c.saw_prev_marked); assert!(c.saw_prev_marked);
} }
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 4: { h -> (hmk0,hmv0,hmk1,hmv1), {hmk0,hmv0,hmk1,hmv1} -> h } // Cycle 4: { h -> (hmk0,hmv0,hmk1,hmv1), {hmk0,hmv0,hmk1,hmv1} -> h }
@ -216,7 +216,7 @@ pub fn main() {
// break; // break;
} }
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 5: { vd[0] -> vd[1], vd[1] -> vd[0] }; // Cycle 5: { vd[0] -> vd[1], vd[1] -> vd[0] };
// does not exercise vd itself // does not exercise vd itself
@ -232,7 +232,7 @@ pub fn main() {
vd[0].descend_into_self(&mut c); vd[0].descend_into_self(&mut c);
assert!(c.saw_prev_marked); assert!(c.saw_prev_marked);
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 6: { vd -> (vd0, vd1), {vd0, vd1} -> vd } // Cycle 6: { vd -> (vd0, vd1), {vd0, vd1} -> vd }
let mut vd: VecDeque<VD> = VecDeque::new(); let mut vd: VecDeque<VD> = VecDeque::new();
@ -247,7 +247,7 @@ pub fn main() {
vd[0].descend_into_self(&mut c); vd[0].descend_into_self(&mut c);
assert!(c.saw_prev_marked); assert!(c.saw_prev_marked);
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 7: { vm -> (vm0, vm1), {vm0, vm1} -> vm } // Cycle 7: { vm -> (vm0, vm1), {vm0, vm1} -> vm }
let mut vm: HashMap<usize, VM> = HashMap::new(); let mut vm: HashMap<usize, VM> = HashMap::new();
@ -262,7 +262,7 @@ pub fn main() {
vm[&0].descend_into_self(&mut c); vm[&0].descend_into_self(&mut c);
assert!(c.saw_prev_marked); assert!(c.saw_prev_marked);
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 8: { ll -> (ll0, ll1), {ll0, ll1} -> ll } // Cycle 8: { ll -> (ll0, ll1), {ll0, ll1} -> ll }
let mut ll: LinkedList<LL> = LinkedList::new(); let mut ll: LinkedList<LL> = LinkedList::new();
@ -282,7 +282,7 @@ pub fn main() {
// break; // break;
} }
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 9: { bh -> (bh0, bh1), {bh0, bh1} -> bh } // Cycle 9: { bh -> (bh0, bh1), {bh0, bh1} -> bh }
let mut bh: BinaryHeap<BH> = BinaryHeap::new(); let mut bh: BinaryHeap<BH> = BinaryHeap::new();
@ -302,7 +302,7 @@ pub fn main() {
// break; // break;
} }
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 10: { btm -> (btk0, btv1), {bt0, bt1} -> btm } // Cycle 10: { btm -> (btk0, btv1), {bt0, bt1} -> btm }
let mut btm: BTreeMap<BTM, BTM> = BTreeMap::new(); let mut btm: BTreeMap<BTM, BTM> = BTreeMap::new();
@ -323,7 +323,7 @@ pub fn main() {
// break; // break;
} }
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 10: { bts -> (bts0, bts1), {bts0, bts1} -> btm } // Cycle 10: { bts -> (bts0, bts1), {bts0, bts1} -> btm }
let mut bts: BTreeSet<BTS> = BTreeSet::new(); let mut bts: BTreeSet<BTS> = BTreeSet::new();
@ -343,7 +343,7 @@ pub fn main() {
// break; // break;
} }
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 11: { rc0 -> (rc1, rc2), rc1 -> (), rc2 -> rc0 } // Cycle 11: { rc0 -> (rc1, rc2), rc1 -> (), rc2 -> rc0 }
let (rc0, rc1, rc2): (RCRC, RCRC, RCRC); let (rc0, rc1, rc2): (RCRC, RCRC, RCRC);
@ -361,7 +361,7 @@ pub fn main() {
rc0.descend_into_self(&mut c); rc0.descend_into_self(&mut c);
assert!(c.saw_prev_marked); assert!(c.saw_prev_marked);
if PRINT { println!(""); } if PRINT { println!(); }
// We want to take the previous Rc case and generalize it to Arc. // 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); arc0.descend_into_self(&mut c);
assert!(c.saw_prev_marked); assert!(c.saw_prev_marked);
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 13: { arc0 -> (arc1, arc2), arc1 -> (), arc2 -> arc0 }, rwlocks // Cycle 13: { arc0 -> (arc1, arc2), arc1 -> (), arc2 -> arc0 }, rwlocks
let (arc0, arc1, arc2): (ARCRW, ARCRW, ARCRW); let (arc0, arc1, arc2): (ARCRW, ARCRW, ARCRW);
@ -413,7 +413,7 @@ pub fn main() {
arc0.descend_into_self(&mut c); arc0.descend_into_self(&mut c);
assert!(c.saw_prev_marked); assert!(c.saw_prev_marked);
if PRINT { println!(""); } if PRINT { println!(); }
// Cycle 14: { arc0 -> (arc1, arc2), arc1 -> (), arc2 -> arc0 }, mutexs // Cycle 14: { arc0 -> (arc1, arc2), arc1 -> (), arc2 -> arc0 }, mutexs
let (arc0, arc1, arc2): (ARCM, ARCM, ARCM); let (arc0, arc1, arc2): (ARCM, ARCM, ARCM);

View File

@ -6,6 +6,6 @@ fn main() {
//~| NOTE `&str` is not an iterator //~| NOTE `&str` is not an iterator
//~| HELP the trait `std::iter::Iterator` is not implemented for `&str` //~| HELP the trait `std::iter::Iterator` is not implemented for `&str`
//~| NOTE required by `std::iter::IntoIterator::into_iter` //~| NOTE required by `std::iter::IntoIterator::into_iter`
println!(""); println!();
} }
} }

View File

@ -7,3 +7,5 @@ edition = "2018"
[dependencies] [dependencies]
toml = "0.5" toml = "0.5"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = "0.9"

View File

@ -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 toml;
use serde::Serialize; use serde::Serialize;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::env; use std::env;
use std::fs; use std::fs;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write, BufRead, BufReader};
use std::path::{PathBuf, Path}; use std::path::{PathBuf, Path};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::collections::HashMap;
static HOSTS: &[&str] = &[ static HOSTS: &[&str] = &[
"aarch64-unknown-linux-gnu", "aarch64-unknown-linux-gnu",
@ -146,6 +153,9 @@ static MINGW: &[&str] = &[
"x86_64-pc-windows-gnu", "x86_64-pc-windows-gnu",
]; ];
static TOOLSTATE: &str =
"https://raw.githubusercontent.com/rust-lang-nursery/rust-toolstate/master/history/linux.tsv";
#[derive(Serialize)] #[derive(Serialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
struct Manifest { struct Manifest {
@ -270,6 +280,7 @@ fn main() {
// Do not ask for a passphrase while manually testing // Do not ask for a passphrase while manually testing
let mut passphrase = String::new(); let mut passphrase = String::new();
if should_sign { if should_sign {
// `x.py` passes the passphrase via stdin.
t!(io::stdin().read_to_string(&mut passphrase)); 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.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.miri_git_commit_hash = self.git_commit_hash("miri", "x86_64-unknown-linux-gnu");
self.check_toolstate();
self.digest_and_sign(); self.digest_and_sign();
let manifest = self.build_manifest(); let manifest = self.build_manifest();
self.write_channel_files(&self.rust_release, &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) { fn digest_and_sign(&mut self) {
for file in t!(self.input.read_dir()).map(|e| t!(e).path()) { for file in t!(self.input.read_dir()).map(|e| t!(e).path()) {
let filename = file.file_name().unwrap().to_str().unwrap(); let filename = file.file_name().unwrap().to_str().unwrap();
@ -532,19 +575,20 @@ impl Builder {
.as_ref() .as_ref()
.cloned() .cloned()
.map(|version| (version, true)) .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: // Miri is nightly-only; never ship it for other trains.
// <https://github.com/japaric/xargo/pull/204#issuecomment-374888868>
if pkgname == "miri-preview" && self.rust_release != "nightly" { 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| { let targets = targets.iter().map(|name| {
if is_present { if is_present {
// The component generally exists, but it might still be missing for this target.
let filename = self.filename(pkgname, name); let filename = self.filename(pkgname, name);
let digest = match self.digests.remove(&filename) { let digest = match self.digests.remove(&filename) {
Some(digest) => digest, Some(digest) => digest,
// This component does not exist for this target -- skip it.
None => return (name.to_string(), Target::unavailable()), None => return (name.to_string(), Target::unavailable()),
}; };
let xz_filename = filename.replace(".tar.gz", ".tar.xz"); let xz_filename = filename.replace(".tar.gz", ".tar.xz");

View File

@ -253,7 +253,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
if args.len() == 1 || args[1] == "-h" || args[1] == "--help" { if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0); let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
println!("{}", opts.usage(&message)); println!("{}", opts.usage(&message));
println!(""); println!();
panic!() panic!()
} }
@ -265,7 +265,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
if matches.opt_present("h") || matches.opt_present("help") { if matches.opt_present("h") || matches.opt_present("help") {
let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0); let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
println!("{}", opts.usage(&message)); println!("{}", opts.usage(&message));
println!(""); println!();
panic!() panic!()
} }

View File

@ -2593,7 +2593,7 @@ impl<'test> TestCx<'test> {
" actual: {}", " actual: {}",
codegen_units_to_str(&actual_item.codegen_units) codegen_units_to_str(&actual_item.codegen_units)
); );
println!(""); println!();
} }
} }
@ -3526,7 +3526,7 @@ impl<'test> TestCx<'test> {
} }
} }
} }
println!(""); println!();
} }
} }
} }

View File

@ -7,6 +7,8 @@
## It is set as callback for `src/ci/docker/x86_64-gnu-tools/repo.sh` by the CI scripts ## 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`). ## when a new commit lands on `master` (i.e., after it passed all checks on `auto`).
from __future__ import print_function
import sys import sys
import re import re
import os import os
@ -20,21 +22,26 @@ except ImportError:
import urllib.request as urllib2 import urllib.request as urllib2
# List of people to ping when the status of a tool or a book changed. # 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 = { MAINTAINERS = {
'miri': '@oli-obk @RalfJung @eddyb', 'miri': {'oli-obk', 'RalfJung', 'eddyb'},
'clippy-driver': '@Manishearth @llogiq @mcarton @oli-obk @phansch @flip1995 @yaahc', 'clippy-driver': {
'rls': '@Xanewok', 'Manishearth', 'llogiq', 'mcarton', 'oli-obk', 'phansch', 'flip1995',
'rustfmt': '@topecongiro', 'yaahc',
'book': '@carols10cents @steveklabnik', },
'nomicon': '@frewsxcv @Gankro', 'rls': {'Xanewok'},
'reference': '@steveklabnik @Havvy @matthewjasper @ehuss', 'rustfmt': {'topecongiro'},
'rust-by-example': '@steveklabnik @marioidival @projektir', 'book': {'carols10cents', 'steveklabnik'},
'embedded-book': ( 'nomicon': {'frewsxcv', 'Gankra'},
'@adamgreig @andre-richter @jamesmunns @korken89 ' 'reference': {'steveklabnik', 'Havvy', 'matthewjasper', 'ehuss'},
'@ryankurte @thejpster @therealprof' 'rust-by-example': {'steveklabnik', 'marioidival'},
), 'embedded-book': {
'edition-guide': '@ehuss @Centril @steveklabnik', 'adamgreig', 'andre-richter', 'jamesmunns', 'korken89',
'rustc-guide': '@mark-i-m @spastorino @amanjeev' 'ryankurte', 'thejpster', 'therealprof',
},
'edition-guide': {'ehuss', 'Centril', 'steveklabnik'},
'rustc-guide': {'mark-i-m', 'spastorino', 'amanjeev'},
} }
REPOS = { 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): def read_current_status(current_commit, path):
'''Reads build status of `current_commit` from content of `history/*.tsv` '''Reads build status of `current_commit` from content of `history/*.tsv`
''' '''
@ -73,13 +124,12 @@ def maybe_delink(message):
def issue( def issue(
tool, tool,
status, status,
maintainers, assignees,
relevant_pr_number, relevant_pr_number,
relevant_pr_user, relevant_pr_user,
pr_reviewer, pr_reviewer,
): ):
# Open an issue about the toolstate failure. # Open an issue about the toolstate failure.
assignees = [x.strip() for x in maintainers.split('@') if x != '']
if status == 'test-fail': if status == 'test-fail':
status_description = 'has failing tests' status_description = 'has failing tests'
else: else:
@ -100,7 +150,7 @@ def issue(
REPOS.get(tool), relevant_pr_user, pr_reviewer REPOS.get(tool), relevant_pr_user, pr_reviewer
)), )),
'title': '`{}` no longer builds after {}'.format(tool, relevant_pr_number), 'title': '`{}` no longer builds after {}'.format(tool, relevant_pr_number),
'assignees': assignees, 'assignees': list(assignees),
'labels': ['T-compiler', 'I-nominated'], 'labels': ['T-compiler', 'I-nominated'],
}) })
print("Creating issue:\n{}".format(request)) print("Creating issue:\n{}".format(request))
@ -150,18 +200,19 @@ def update_latest(
old = status[os] old = status[os]
new = s.get(tool, old) new = s.get(tool, old)
status[os] = new status[os] = new
maintainers = ' '.join('@'+name for name in MAINTAINERS[tool])
if new > old: # comparing the strings, but they are ordered appropriately! if new > old: # comparing the strings, but they are ordered appropriately!
# things got fixed or at least the status quo improved # things got fixed or at least the status quo improved
changed = True changed = True
message += '🎉 {} on {}: {}{} (cc {}, @rust-lang/infra).\n' \ message += '🎉 {} on {}: {}{} (cc {}, @rust-lang/infra).\n' \
.format(tool, os, old, new, MAINTAINERS.get(tool)) .format(tool, os, old, new, maintainers)
elif new < old: elif new < old:
# tests or builds are failing and were not failing before # tests or builds are failing and were not failing before
changed = True changed = True
title = '💔 {} on {}: {}{}' \ title = '💔 {} on {}: {}{}' \
.format(tool, os, old, new) .format(tool, os, old, new)
message += '{} (cc {}, @rust-lang/infra).\n' \ message += '{} (cc {}, @rust-lang/infra).\n' \
.format(title, MAINTAINERS.get(tool)) .format(title, maintainers)
# Most tools only create issues for build failures. # Most tools only create issues for build failures.
# Other failures can be spurious. # Other failures can be spurious.
if new == 'build-fail' or (tool == 'miri' and new == 'test-fail'): if new == 'build-fail' or (tool == 'miri' and new == 'test-fail'):
@ -200,6 +251,16 @@ def update_latest(
if __name__ == '__main__': 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_commit = sys.argv[1]
cur_datetime = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') cur_datetime = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
cur_commit_msg = sys.argv[2] cur_commit_msg = sys.argv[2]