Skip to content

Experiment: Apply #[deprecated_safe] to env::set_var/env::remove_var #95942

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ impl rustc_driver::Callbacks for CraneliftPassesCallbacks {

fn main() {
rustc_driver::init_rustc_env_logger();
rustc_driver::install_ice_hook();
// SAFETY: in main(), no other threads could be reading or writing the environment
// FIXME(skippy) are there definitely zero threads here? other functions have been called
unsafe {
rustc_driver::install_ice_hook();
}
let exit_code = rustc_driver::catch_with_exit_code(|| {
let mut use_clif = false;

Expand Down
12 changes: 10 additions & 2 deletions compiler/rustc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,11 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
/// Installs a panic hook that will print the ICE message on unexpected panics.
///
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
pub fn install_ice_hook() {
///
/// # Safety
///
/// Must not be called when any other thread could be reading or writing the environment.
pub unsafe fn install_ice_hook() {
// If the user has not explicitly overridden "RUST_BACKTRACE", then produce
// full backtraces. When a compiler ICE happens, we want to gather
// as much information as possible to present in the issue opened
Expand Down Expand Up @@ -1320,7 +1324,11 @@ pub fn main() -> ! {
init_rustc_env_logger();
signal_handler::install();
let mut callbacks = TimePassesCallbacks::default();
install_ice_hook();
// SAFETY: in main(), no other threads could be reading or writing the environment
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// SAFETY: Lack of thread conflicts asserted by the caller

// FIXME(skippy) are there definitely zero threads here? other functions have been called
unsafe {
install_ice_hook();
}
let exit_code = catch_with_exit_code(|| {
let args = env::args_os()
.enumerate()
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use super::UnstableFeatures;
#[test]
fn rustc_bootstrap_parsing() {
let is_bootstrap = |env, krate| {
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
std::env::set_var("RUSTC_BOOTSTRAP", env);
matches!(UnstableFeatures::from_environment(krate), UnstableFeatures::Cheat)
};
Expand Down
24 changes: 16 additions & 8 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,17 @@ pub fn configure_and_expand(
new_path.push(path);
}
}
env::set_var(
"PATH",
&env::join_paths(
new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()),
)
.unwrap(),
);
// SAFETY: we're in a cfg!(windows) section, set_var is always sound
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
env::set_var(
"PATH",
&env::join_paths(
new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()),
)
.unwrap(),
);
}
}

// Create the config for macro expansion
Expand Down Expand Up @@ -367,7 +371,11 @@ pub fn configure_and_expand(
resolver.lint_buffer().buffer_lint(lint, node_id, span, msg);
}
if cfg!(windows) {
env::set_var("PATH", &old_path);
// SAFETY: we're in a cfg!(windows) section, set_var is always sound
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
env::set_var("PATH", &old_path);
}
}

if recursion_limit_hit {
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_llvm/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ fn detect_llvm_link() -> (&'static str, &'static str) {
// the one we want to use. As such, we restore the environment to what bootstrap saw. This isn't
// perfect -- we might actually want to see something from Cargo's added library paths -- but
// for now it works.
fn restore_library_path() {
// SAFETY: must not be called when any other thread could be reading or writing the environment
unsafe fn restore_library_path() {
let key = tracked_env_var_os("REAL_LIBRARY_PATH_VAR").expect("REAL_LIBRARY_PATH_VAR");
if let Some(env) = tracked_env_var_os("REAL_LIBRARY_PATH") {
env::set_var(&key, &env);
Expand Down Expand Up @@ -81,7 +82,10 @@ fn main() {
return;
}

restore_library_path();
// SAFETY: in main(), no other threads could be reading or writing the environment
unsafe {
restore_library_path();
}

let target = env::var("TARGET").expect("TARGET was not set");
let llvm_config =
Expand Down
5 changes: 4 additions & 1 deletion library/std/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,10 @@ pub struct JoinPathsError {
/// let mut paths = env::split_paths(&path).collect::<Vec<_>>();
/// paths.push(PathBuf::from("/home/xyz/bin"));
/// let new_path = env::join_paths(paths)?;
/// env::set_var("PATH", &new_path);
/// // SAFETY: in main(), no other threads could be reading or writing the environment
/// unsafe {
/// env::set_var("PATH", &new_path);
/// }
/// }
///
/// Ok(())
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/process/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,12 @@ fn test_capture_env_at_spawn() {

// This variable will not be present if the environment has already
// been captured above.
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
env::set_var("RUN_TEST_NEW_ENV2", "456");
let result = cmd.output().unwrap();
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
env::remove_var("RUN_TEST_NEW_ENV2");

let output = String::from_utf8_lossy(&result.stdout).to_string();
Expand Down
70 changes: 60 additions & 10 deletions library/std/tests/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,36 @@ fn eq(a: Option<OsString>, b: Option<&str>) {
#[test]
fn test_set_var() {
let n = make_rand_name();
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
set_var(&n, "VALUE");
eq(var_os(&n), Some("VALUE"));
}

#[test]
fn test_remove_var() {
let n = make_rand_name();
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
set_var(&n, "VALUE");
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
remove_var(&n);
eq(var_os(&n), None);
}

#[test]
fn test_set_var_overwrite() {
let n = make_rand_name();
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
set_var(&n, "1");
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
set_var(&n, "2");
eq(var_os(&n), Some("2"));
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
set_var(&n, "");
eq(var_os(&n), Some(""));
}
Expand All @@ -51,6 +63,8 @@ fn test_var_big() {
i += 1;
}
let n = make_rand_name();
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
set_var(&n, &s);
eq(var_os(&n), Some(&s));
}
Expand All @@ -60,8 +74,12 @@ fn test_var_big() {
fn test_env_set_get_huge() {
let n = make_rand_name();
let s = "x".repeat(10000);
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
set_var(&n, &s);
eq(var_os(&n), Some(&s));
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
remove_var(&n);
eq(var_os(&n), None);
}
Expand All @@ -71,6 +89,8 @@ fn test_env_set_var() {
let n = make_rand_name();

let mut e = vars_os();
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
set_var(&n, "VALUE");
assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" }));

Expand All @@ -95,9 +115,13 @@ fn env_home_dir() {
if #[cfg(unix)] {
let oldhome = var_to_os_string(var("HOME"));

// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
set_var("HOME", "/home/MountainView");
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));

// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
remove_var("HOME");
if cfg!(target_os = "android") {
assert!(home_dir().is_none());
Expand All @@ -108,33 +132,59 @@ fn env_home_dir() {
assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView")));
}

// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
if let Some(oldhome) = oldhome { set_var("HOME", oldhome); }
} else if #[cfg(windows)] {
let oldhome = var_to_os_string(var("HOME"));
let olduserprofile = var_to_os_string(var("USERPROFILE"));

remove_var("HOME");
remove_var("USERPROFILE");
// SAFETY: inside a cfg!(windows) section, remove_var is always sound
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
remove_var("HOME");
remove_var("USERPROFILE");
}

assert!(home_dir().is_some());

set_var("HOME", "/home/MountainView");
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));

remove_var("HOME");
// SAFETY: inside a cfg!(windows) section, remove_var is always sound
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
remove_var("HOME");
}

set_var("USERPROFILE", "/home/MountainView");
// SAFETY: inside a cfg!(windows) section, set_var is always sound
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
set_var("USERPROFILE", "/home/MountainView");
}
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));

set_var("HOME", "/home/MountainView");
set_var("USERPROFILE", "/home/PaloAlto");
// SAFETY: inside a cfg!(windows) section, set_var is always sound
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
set_var("HOME", "/home/MountainView");
set_var("USERPROFILE", "/home/PaloAlto");
}
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));

remove_var("HOME");
remove_var("USERPROFILE");
// SAFETY: inside a cfg!(windows) section, remove_var is always sound
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
remove_var("HOME");
remove_var("USERPROFILE");
}

if let Some(oldhome) = oldhome { set_var("HOME", oldhome); }
if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); }
// SAFETY: inside a cfg!(windows) section, set_var is always sound
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
if let Some(oldhome) = oldhome { set_var("HOME", oldhome); }
if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); }
}
}
}
}
2 changes: 2 additions & 0 deletions library/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) {
// If we're being run in SpawnedSecondary mode, run the test here. run_test
// will then exit the process.
if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) {
// FIXME(skippy) deprecate_safe: should be a single thread here, assert that somehow?
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
env::remove_var(SECONDARY_TEST_INVOKER_VAR);
let test = tests
.iter()
Expand Down
4 changes: 4 additions & 0 deletions library/test/src/term/terminfo/searcher/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ fn test_get_dbpath_for_term() {
}
assert!(x("screen") == "/usr/share/terminfo/s/screen");
assert!(get_dbpath_for_term("") == None);
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style of cfg_attrs is suspicious - aren't the bootstrap cfgs going to be deleted at the next upgrade?

Instead, use // SAFETY: FIXME(skippy) This code is incorrect.

env::set_var("TERMINFO_DIRS", ":");
assert!(x("screen") == "/usr/share/terminfo/s/screen");
// FIXME(skippy) there's no fix for deprecated_safe until tests can be run single threaded
#[cfg_attr(not(bootstrap), allow(deprecated_safe))]
env::remove_var("TERMINFO_DIRS");
}
6 changes: 5 additions & 1 deletion src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,11 @@ pub fn main() {
}

rustc_driver::set_sigpipe_handler();
rustc_driver::install_ice_hook();
// SAFETY: in main(), no other threads could be reading or writing the environment
// FIXME(skippy) are there definitely zero threads here? other functions have been called
unsafe {
rustc_driver::install_ice_hook();
}

// When using CI artifacts (with `download_stage1 = true`), tracing is unconditionally built
// with `--features=static_max_level_info`, which disables almost all rustdoc logging. To avoid
Expand Down
6 changes: 5 additions & 1 deletion src/test/ui/command/command-exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ fn main() {
}

"exec-test5" => {
env::set_var("VARIABLE", "ABC");
// SAFETY: in main(), no other threads could be reading or writing the environment
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
env::set_var("VARIABLE", "ABC");
}
Command::new("definitely-not-a-real-binary").env("VARIABLE", "XYZ").exec();
assert_eq!(env::var("VARIABLE").unwrap(), "ABC");
println!("passed");
Expand Down
16 changes: 12 additions & 4 deletions src/test/ui/process/process-envs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ fn main() {
// save original environment
let old_env = env::var_os("RUN_TEST_NEW_ENV");

env::set_var("RUN_TEST_NEW_ENV", "123");
// SAFETY: in main(), no other threads could be reading or writing the environment
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
env::set_var("RUN_TEST_NEW_ENV", "123");
}

// create filtered environment vector
let filtered_env : HashMap<String, String> =
Expand All @@ -40,9 +44,13 @@ fn main() {
cmd.envs(&filtered_env);

// restore original environment
match old_env {
None => env::remove_var("RUN_TEST_NEW_ENV"),
Some(val) => env::set_var("RUN_TEST_NEW_ENV", &val)
// SAFETY: in main(), no other threads could be reading or writing the environment
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
match old_env {
None => env::remove_var("RUN_TEST_NEW_ENV"),
Some(val) => env::set_var("RUN_TEST_NEW_ENV", &val)
}
}

let result = cmd.output().unwrap();
Expand Down
16 changes: 12 additions & 4 deletions src/test/ui/process/process-remove-from-env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,23 @@ fn main() {
// save original environment
let old_env = env::var_os("RUN_TEST_NEW_ENV");

env::set_var("RUN_TEST_NEW_ENV", "123");
// SAFETY: in main(), no other threads could be reading or writing the environment
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
env::set_var("RUN_TEST_NEW_ENV", "123");
}

let mut cmd = env_cmd();
cmd.env_remove("RUN_TEST_NEW_ENV");

// restore original environment
match old_env {
None => env::remove_var("RUN_TEST_NEW_ENV"),
Some(val) => env::set_var("RUN_TEST_NEW_ENV", &val)
// SAFETY: in main(), no other threads could be reading or writing the environment
#[cfg_attr(bootstrap, allow(unused_unsafe))]
unsafe {
match old_env {
None => env::remove_var("RUN_TEST_NEW_ENV"),
Some(val) => env::set_var("RUN_TEST_NEW_ENV", &val)
}
}

let result = cmd.output().unwrap();
Expand Down
Loading