Rollup merge of #130883 - madsmtm:env-var-query, r=petrochenkov
Add environment variable query Generally, `rustc` prefers command-line arguments, but in some cases, an environment variable really is the most sensible option. We should make sure that this works properly with the compiler's change-tracking mechanisms, such that changing the relevant environment variable causes a rebuild. This PR is a first step forwards in doing that. Part of the work needed to do https://github.com/rust-lang/rust/issues/118204, see https://github.com/rust-lang/rust/pull/129342 for some discussion. r? ``@petrochenkov``
This commit is contained in:
commit
c45986ae61
10 changed files with 118 additions and 7 deletions
|
@ -1,9 +1,9 @@
|
|||
//! The entry point of the NLL borrow checker.
|
||||
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
use std::{env, io};
|
||||
|
||||
use polonius_engine::{Algorithm, Output};
|
||||
use rustc_index::IndexSlice;
|
||||
|
@ -162,9 +162,8 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
}
|
||||
|
||||
if polonius_output {
|
||||
let algorithm =
|
||||
env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid"));
|
||||
let algorithm = Algorithm::from_str(&algorithm).unwrap();
|
||||
let algorithm = infcx.tcx.env_var("POLONIUS_ALGORITHM").unwrap_or("Hybrid");
|
||||
let algorithm = Algorithm::from_str(algorithm).unwrap();
|
||||
debug!("compute_regions: using polonius algorithm {:?}", algorithm);
|
||||
let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis");
|
||||
Some(Box::new(Output::compute(polonius_facts, algorithm, false)))
|
||||
|
|
|
@ -564,6 +564,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl_stable_traits_for_trivial_type!(::std::ffi::OsStr);
|
||||
|
||||
impl_stable_traits_for_trivial_type!(::std::path::Path);
|
||||
impl_stable_traits_for_trivial_type!(::std::path::PathBuf);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::any::Any;
|
||||
use std::ffi::OsString;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::io::{self, BufWriter, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, LazyLock, OnceLock};
|
||||
|
@ -361,6 +361,31 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) {
|
|||
)
|
||||
}
|
||||
|
||||
fn env_var_os<'tcx>(tcx: TyCtxt<'tcx>, key: &'tcx OsStr) -> Option<&'tcx OsStr> {
|
||||
let value = env::var_os(key);
|
||||
|
||||
let value_tcx = value.as_ref().map(|value| {
|
||||
let encoded_bytes = tcx.arena.alloc_slice(value.as_encoded_bytes());
|
||||
debug_assert_eq!(value.as_encoded_bytes(), encoded_bytes);
|
||||
// SAFETY: The bytes came from `as_encoded_bytes`, and we assume that
|
||||
// `alloc_slice` is implemented correctly, and passes the same bytes
|
||||
// back (debug asserted above).
|
||||
unsafe { OsStr::from_encoded_bytes_unchecked(encoded_bytes) }
|
||||
});
|
||||
|
||||
// Also add the variable to Cargo's dependency tracking
|
||||
//
|
||||
// NOTE: This only works for passes run before `write_dep_info`. See that
|
||||
// for extension points for configuring environment variables to be
|
||||
// properly change-tracked.
|
||||
tcx.sess.psess.env_depinfo.borrow_mut().insert((
|
||||
Symbol::intern(&key.to_string_lossy()),
|
||||
value.as_ref().and_then(|value| value.to_str()).map(|value| Symbol::intern(&value)),
|
||||
));
|
||||
|
||||
value_tcx
|
||||
}
|
||||
|
||||
// Returns all the paths that correspond to generated files.
|
||||
fn generated_output_paths(
|
||||
tcx: TyCtxt<'_>,
|
||||
|
@ -725,6 +750,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
|
|||
|tcx, _| tcx.arena.alloc_from_iter(tcx.resolutions(()).stripped_cfg_items.steal());
|
||||
providers.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).1;
|
||||
providers.early_lint_checks = early_lint_checks;
|
||||
providers.env_var_os = env_var_os;
|
||||
limits::provide(providers);
|
||||
proc_macro_decls::provide(providers);
|
||||
rustc_const_eval::provide(providers);
|
||||
|
|
|
@ -104,8 +104,10 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
|
|||
// determining if we are in a doctest context can't currently be determined
|
||||
// by the code itself (there are no specific attributes), but fortunately rustdoc
|
||||
// sets a perma-unstable env var for libtest so we just reuse that for now
|
||||
let is_at_toplevel_doctest =
|
||||
|| self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok();
|
||||
let is_at_toplevel_doctest = || {
|
||||
self.body_depth == 2
|
||||
&& cx.tcx.env_var_os("UNSTABLE_RUSTDOC_TEST_PATH".as_ref()).is_some()
|
||||
};
|
||||
|
||||
match item.kind {
|
||||
ItemKind::Impl(impl_) => {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::ffi::OsStr;
|
||||
use std::intrinsics::transmute_unchecked;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
|
@ -67,6 +68,10 @@ impl<T> EraseType for &'_ [T] {
|
|||
type Result = [u8; size_of::<&'static [()]>()];
|
||||
}
|
||||
|
||||
impl EraseType for &'_ OsStr {
|
||||
type Result = [u8; size_of::<&'static OsStr>()];
|
||||
}
|
||||
|
||||
impl<T> EraseType for &'_ ty::List<T> {
|
||||
type Result = [u8; size_of::<&'static ty::List<()>>()];
|
||||
}
|
||||
|
@ -174,6 +179,10 @@ impl<T> EraseType for Option<&'_ [T]> {
|
|||
type Result = [u8; size_of::<Option<&'static [()]>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Option<&'_ OsStr> {
|
||||
type Result = [u8; size_of::<Option<&'static OsStr>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Option<mir::DestructuredConstant<'_>> {
|
||||
type Result = [u8; size_of::<Option<mir::DestructuredConstant<'static>>>()];
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Defines the set of legal keys that can be used in queries.
|
||||
|
||||
use std::ffi::OsStr;
|
||||
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, ModDefId};
|
||||
use rustc_hir::hir_id::{HirId, OwnerId};
|
||||
use rustc_query_system::dep_graph::DepNodeIndex;
|
||||
|
@ -498,6 +500,14 @@ impl Key for Option<Symbol> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Key for &'tcx OsStr {
|
||||
type Cache<V> = DefaultCache<Self, V>;
|
||||
|
||||
fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
|
||||
DUMMY_SP
|
||||
}
|
||||
}
|
||||
|
||||
/// Canonical query goals correspond to abstract trait operations that
|
||||
/// are not tied to any crate in particular.
|
||||
impl<'tcx, T: Clone> Key for CanonicalQueryInput<'tcx, T> {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#![allow(unused_parens)]
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -119,6 +120,21 @@ rustc_queries! {
|
|||
desc { "perform lints prior to AST lowering" }
|
||||
}
|
||||
|
||||
/// Tracked access to environment variables.
|
||||
///
|
||||
/// Useful for the implementation of `std::env!`, `proc-macro`s change
|
||||
/// detection and other changes in the compiler's behaviour that is easier
|
||||
/// to control with an environment variable than a flag.
|
||||
///
|
||||
/// NOTE: This currently does not work with dependency info in the
|
||||
/// analysis, codegen and linking passes, place extra code at the top of
|
||||
/// `rustc_interface::passes::write_dep_info` to make that work.
|
||||
query env_var_os(key: &'tcx OsStr) -> Option<&'tcx OsStr> {
|
||||
// Environment variables are global state
|
||||
eval_always
|
||||
desc { "get the value of an environment variable" }
|
||||
}
|
||||
|
||||
query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt {
|
||||
no_hash
|
||||
desc { "getting the resolver outputs" }
|
||||
|
|
|
@ -7,6 +7,8 @@ pub mod tls;
|
|||
use std::assert_matches::{assert_matches, debug_assert_matches};
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::Ordering;
|
||||
use std::env::VarError;
|
||||
use std::ffi::OsStr;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Bound, Deref};
|
||||
|
@ -1883,6 +1885,15 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Helper to get a tracked environment variable via. [`TyCtxt::env_var_os`] and converting to
|
||||
/// UTF-8 like [`std::env::var`].
|
||||
pub fn env_var<K: ?Sized + AsRef<OsStr>>(self, key: &'tcx K) -> Result<&'tcx str, VarError> {
|
||||
match self.env_var_os(key.as_ref()) {
|
||||
Some(value) => value.to_str().ok_or_else(|| VarError::NotUnicode(value.to_os_string())),
|
||||
None => Err(VarError::NotPresent),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TyCtxtAt<'tcx> {
|
||||
|
|
18
tests/incremental/env/env_macro.rs
vendored
Normal file
18
tests/incremental/env/env_macro.rs
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Check that changes to environment variables are propagated to `env!`.
|
||||
//
|
||||
// This test is intentionally written to not use any `#[cfg(rpass*)]`, to
|
||||
// _really_ test that we re-compile if the environment variable changes.
|
||||
|
||||
//@ revisions: cfail1 rpass2 rpass3 cfail4
|
||||
//@ [cfail1]unset-rustc-env:EXAMPLE_ENV
|
||||
//@ [rpass2]rustc-env:EXAMPLE_ENV=one
|
||||
//@ [rpass2]exec-env:EXAMPLE_ENV=one
|
||||
//@ [rpass3]rustc-env:EXAMPLE_ENV=two
|
||||
//@ [rpass3]exec-env:EXAMPLE_ENV=two
|
||||
//@ [cfail4]unset-rustc-env:EXAMPLE_ENV
|
||||
|
||||
fn main() {
|
||||
assert_eq!(env!("EXAMPLE_ENV"), std::env::var("EXAMPLE_ENV").unwrap());
|
||||
//[cfail1]~^ ERROR environment variable `EXAMPLE_ENV` not defined at compile time
|
||||
//[cfail4]~^^ ERROR environment variable `EXAMPLE_ENV` not defined at compile time
|
||||
}
|
18
tests/incremental/env/option_env_macro.rs
vendored
Normal file
18
tests/incremental/env/option_env_macro.rs
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Check that changes to environment variables are propagated to `option_env!`.
|
||||
//
|
||||
// This test is intentionally written to not use any `#[cfg(rpass*)]`, to
|
||||
// _really_ test that we re-compile if the environment variable changes.
|
||||
|
||||
//@ revisions: rpass1 rpass2 rpass3 rpass4
|
||||
//@ [rpass1]unset-rustc-env:EXAMPLE_ENV
|
||||
//@ [rpass1]unset-exec-env:EXAMPLE_ENV
|
||||
//@ [rpass2]rustc-env:EXAMPLE_ENV=one
|
||||
//@ [rpass2]exec-env:EXAMPLE_ENV=one
|
||||
//@ [rpass3]rustc-env:EXAMPLE_ENV=two
|
||||
//@ [rpass3]exec-env:EXAMPLE_ENV=two
|
||||
//@ [rpass4]unset-rustc-env:EXAMPLE_ENV
|
||||
//@ [rpass4]unset-exec-env:EXAMPLE_ENV
|
||||
|
||||
fn main() {
|
||||
assert_eq!(option_env!("EXAMPLE_ENV"), std::env::var("EXAMPLE_ENV").ok().as_deref());
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue