Auto merge of #113022 - GuillaumeGomez:rollup-vkpzsuw, r=GuillaumeGomez

Rollup of 4 pull requests

Successful merges:

 - #112918 (display PID of process holding lock)
 - #112990 (Add a regression test for #96699)
 - #113011 (Add enum for `can_access_statics` boolean)
 - #113018 (Fix test for #96258)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-06-25 08:51:49 +00:00
commit 0d03812e24
9 changed files with 172 additions and 54 deletions

View File

@ -14,7 +14,7 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_span::source_map::Span;
use rustc_target::abi::{self, Abi};
use super::{CompileTimeEvalContext, CompileTimeInterpreter};
use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
use crate::errors;
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
@ -93,7 +93,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
tcx: TyCtxt<'tcx>,
root_span: Span,
param_env: ty::ParamEnv<'tcx>,
can_access_statics: bool,
can_access_statics: CanAccessStatics,
) -> CompileTimeEvalContext<'mir, 'tcx> {
debug!("mk_eval_cx: {:?}", param_env);
InterpCx::new(
@ -207,7 +207,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
tcx,
tcx.def_span(key.value.instance.def_id()),
key.param_env,
/*can_access_statics:*/ is_static,
CanAccessStatics::from(is_static),
);
let mplace = ecx.raw_const_to_mplace(constant).expect(
@ -309,7 +309,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
// Statics (and promoteds inside statics) may access other statics, because unlike consts
// they do not have to behave "as if" they were evaluated at runtime.
CompileTimeInterpreter::new(
/*can_access_statics:*/ is_static,
CanAccessStatics::from(is_static),
if tcx.sess.opts.unstable_opts.extra_const_ub_checks {
CheckAlignment::Error
} else {

View File

@ -57,7 +57,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
/// * Interning makes everything outside of statics immutable.
/// * Pointers to allocations inside of statics can never leak outside, to a non-static global.
/// This boolean here controls the second part.
pub(super) can_access_statics: bool,
pub(super) can_access_statics: CanAccessStatics,
/// Whether to check alignment during evaluation.
pub(super) check_alignment: CheckAlignment,
@ -83,8 +83,23 @@ impl CheckAlignment {
}
}
#[derive(Copy, Clone, PartialEq)]
pub(crate) enum CanAccessStatics {
No,
Yes,
}
impl From<bool> for CanAccessStatics {
fn from(value: bool) -> Self {
if value { Self::Yes } else { Self::No }
}
}
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
pub(crate) fn new(can_access_statics: bool, check_alignment: CheckAlignment) -> Self {
pub(crate) fn new(
can_access_statics: CanAccessStatics,
check_alignment: CheckAlignment,
) -> Self {
CompileTimeInterpreter {
num_evaluated_steps: 0,
stack: Vec::new(),
@ -699,7 +714,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
}
} else {
// Read access. These are usually allowed, with some exceptions.
if machine.can_access_statics {
if machine.can_access_statics == CanAccessStatics::Yes {
// Machine configuration allows us read from anything (e.g., `static` initializer).
Ok(())
} else if static_def_id.is_some() {

View File

@ -26,7 +26,7 @@ pub(crate) fn const_caller_location(
(file, line, col): (Symbol, u32, u32),
) -> ConstValue<'_> {
trace!("const_caller_location: {}:{}:{}", file, line, col);
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
let loc_place = ecx.alloc_caller_location(file, line, col);
if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
@ -55,10 +55,12 @@ pub(crate) fn eval_to_valtree<'tcx>(
// FIXME Need to provide a span to `eval_to_valtree`
let ecx = mk_eval_cx(
tcx, DUMMY_SP, param_env,
tcx,
DUMMY_SP,
param_env,
// It is absolutely crucial for soundness that
// we do not read from static items or other mutable memory.
false,
CanAccessStatics::No,
);
let place = ecx.raw_const_to_mplace(const_alloc).unwrap();
debug!(?place);
@ -91,7 +93,7 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
val: mir::ConstantKind<'tcx>,
) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> {
trace!("destructure_mir_constant: {:?}", val);
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
let op = ecx.eval_mir_constant(&val, None, None)?;
// We go to `usize` as we cannot allocate anything bigger anyway.

View File

@ -1,6 +1,7 @@
use super::eval_queries::{mk_eval_cx, op_to_const};
use super::machine::CompileTimeEvalContext;
use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
use crate::const_eval::CanAccessStatics;
use crate::interpret::{
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
MemoryKind, PlaceTy, Scalar,
@ -263,7 +264,11 @@ pub fn valtree_to_const_value<'tcx>(
// FIXME Does this need an example?
let (param_env, ty) = param_env_ty.into_parts();
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
let mut ecx: crate::interpret::InterpCx<
'_,
'_,
crate::const_eval::CompileTimeInterpreter<'_, '_>,
> = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
match ty.kind() {
ty::FnDef(..) => {

View File

@ -2,7 +2,7 @@ use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, Val
use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
use crate::const_eval::{CheckAlignment, CompileTimeInterpreter};
use crate::const_eval::{CanAccessStatics, CheckAlignment, CompileTimeInterpreter};
use crate::interpret::{InterpCx, MemoryKind, OpTy};
/// Determines if this type permits "raw" initialization by just transmuting some memory into an
@ -44,8 +44,7 @@ fn might_permit_raw_init_strict<'tcx>(
tcx: TyCtxt<'tcx>,
kind: ValidityRequirement,
) -> Result<bool, LayoutError<'tcx>> {
let machine =
CompileTimeInterpreter::new(/*can_access_statics:*/ false, CheckAlignment::Error);
let machine = CompileTimeInterpreter::new(CanAccessStatics::No, CheckAlignment::Error);
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);

View File

@ -5,7 +5,9 @@
//! parent directory, and otherwise documentation can be found throughout the `build`
//! directory in each respective module.
use std::env;
use std::fs::OpenOptions;
use std::io::Write;
use std::{env, fs, process};
#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
use bootstrap::t;
@ -20,22 +22,32 @@ fn main() {
#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
let _build_lock_guard;
#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
// Display PID of process holding the lock
// PID will be stored in a lock file
{
let path = config.out.join("lock");
build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(&path)));
let pid = match fs::read_to_string(&path) {
Ok(contents) => contents,
Err(_) => String::new(),
};
build_lock =
fd_lock::RwLock::new(t!(OpenOptions::new().write(true).create(true).open(&path)));
_build_lock_guard = match build_lock.try_write() {
Ok(lock) => lock,
Ok(mut lock) => {
t!(lock.write(&process::id().to_string().as_ref()));
lock
}
err => {
drop(err);
if let Some(pid) = get_lock_owner(&path) {
println!("warning: build directory locked by process {pid}, waiting for lock");
} else {
println!("warning: build directory locked, waiting for lock");
}
t!(build_lock.write())
println!("warning: build directory locked by process {pid}, waiting for lock");
let mut lock = t!(build_lock.write());
t!(lock.write(&process::id().to_string().as_ref()));
lock
}
};
}
#[cfg(any(not(any(unix, windows)), target_os = "solaris"))]
println!("warning: file locking not supported for target, not locking build directory");
@ -108,30 +120,3 @@ fn check_version(config: &Config) -> Option<String> {
Some(msg)
}
/// Get the PID of the process which took the write lock by
/// parsing `/proc/locks`.
#[cfg(target_os = "linux")]
fn get_lock_owner(f: &std::path::Path) -> Option<u64> {
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::os::unix::fs::MetadataExt;
let lock_inode = std::fs::metadata(f).ok()?.ino();
let lockfile = File::open("/proc/locks").ok()?;
BufReader::new(lockfile).lines().find_map(|line| {
// pid--vvvvvv vvvvvvv--- inode
// 21: FLOCK ADVISORY WRITE 359238 08:02:3719774 0 EOF
let line = line.ok()?;
let parts = line.split_whitespace().collect::<Vec<_>>();
let (pid, inode) = (parts[4].parse::<u64>().ok()?, &parts[5]);
let inode = inode.rsplit_once(':')?.1.parse::<u64>().ok()?;
if inode == lock_inode { Some(pid) } else { None }
})
}
#[cfg(not(any(target_os = "linux", target_os = "solaris")))]
fn get_lock_owner(_: &std::path::Path) -> Option<u64> {
// FIXME: Implement on other OS's
None
}

View File

@ -0,0 +1,87 @@
// check-pass
#![allow(dead_code, incomplete_features)]
#![feature(generic_const_exprs)]
const fn min(a: usize, b: usize) -> usize {
if a < b {
a
} else {
b
}
}
trait Trait1<Inner1>
where
Self: Sized,
{
fn crash_here()
where
Inner1: Default,
{
Inner1::default();
}
}
struct Struct1<T>(T);
impl<T> Trait1<T> for Struct1<T> {}
trait Trait2<Inner2>
where
Self: Sized,
{
type Assoc: Trait1<Inner2>;
fn call_crash()
where
Inner2: Default,
{
// if Inner2 implements Default, we can call crash_here.
Self::Assoc::crash_here();
}
}
struct Struct2<const SIZE1: usize, const SIZE2: usize> {}
/*
where
[(); min(SIZE1, SIZE2)]:,
{
elem: [i32; min(SIZE1, SIZE2)],
}
*/
impl<const SIZE1: usize, const SIZE2: usize> Trait2<[i32; min(SIZE1, SIZE2)]>
for Struct2<SIZE1, SIZE2>
{
type Assoc = Struct1<[i32; min(SIZE1, SIZE2)]>;
// dose Struct1<[i32; min(SIZE1, SIZE2)]> implement Default?
}
fn main() {
pattern2();
print_fully_name(<Struct2<1, 2> as Trait2<[i32; min(1, 2)]>>::Assoc::crash_here);
// <compiler_bug2::Struct1<[i32; 1]> as compiler_bug2::Trait1<[i32; 1]>>::crash_here
}
fn pattern1() {
// no crash
<Struct2<1, 2> as Trait2<[i32; min(1, 2)]>>::Assoc::crash_here();
<Struct2<1, 2> as Trait2<[i32; min(1, 2)]>>::call_crash();
}
fn pattern2() {
// crash
<Struct2<1, 2> as Trait2<[i32; min(1, 2)]>>::call_crash();
// undefined reference to `compiler_bug2::Trait1::crash_here'
}
fn pattern3() {
// no crash
<Struct2<1, 2> as Trait2<[i32; min(1, 2)]>>::Assoc::crash_here();
}
fn print_fully_name<T>(_: T) {
let _ = std::any::type_name::<T>();
}

View File

@ -1,4 +1,4 @@
// compile-flags -Wrust-2021-incompatible-closure-captures
#![warn(rust_2021_incompatible_closure_captures)]
fn main() {}
@ -9,7 +9,7 @@ impl Numberer {
//~^ ERROR `async fn` is not permitted in Rust 2015
interval: Duration,
//~^ ERROR cannot find type `Duration` in this scope
) -> Numberer {
) -> Numberer { //~WARN: changes to closure capture in Rust 2021
Numberer {}
}
}

View File

@ -18,7 +18,32 @@ help: consider importing this struct
LL + use std::time::Duration;
|
error: aborting due to 2 previous errors
warning: changes to closure capture in Rust 2021 will affect drop order
--> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-96258.rs:12:19
|
LL | interval: Duration,
| -------- in Rust 2018, this causes the closure to capture `interval`, but in Rust 2021, it has no effect
LL |
LL | ) -> Numberer {
| _________________-_^
| | |
| | in Rust 2018, `interval` is dropped here along with the closure, but in Rust 2021 `interval` is not part of the closure
LL | | Numberer {}
LL | | }
| |_____^
|
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
note: the lint level is defined here
--> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-96258.rs:1:9
|
LL | #![warn(rust_2021_incompatible_closure_captures)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: add a dummy let to cause `interval` to be fully captured
|
LL | ) -> Numberer { let _ = &interval;
| ++++++++++++++++++
error: aborting due to 2 previous errors; 1 warning emitted
Some errors have detailed explanations: E0412, E0670.
For more information about an error, try `rustc --explain E0412`.