Skip to content
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

Add environment variable query #130883

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions compiler/rustc_borrowck/src/nll.rs
Original file line number Diff line number Diff line change
@@ -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_data_structures::fx::FxIndexMap;
Expand Down Expand Up @@ -145,9 +145,11 @@ 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".as_ref())
.unwrap_or_else(|_| "Hybrid".as_ref());
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(all_facts, algorithm, false)))
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_data_structures/src/stable_hasher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,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);

Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_errors/src/diagnostic_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ impl IntoDiagArg for std::ffi::CString {
}
}

impl IntoDiagArg for std::ffi::OsString {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
}
}

impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
Expand Down
31 changes: 30 additions & 1 deletion compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -321,6 +321,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_deref().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.and_then(|value| value.to_str()).map(|value| Symbol::intern(&value)),
));
madsmtm marked this conversation as resolved.
Show resolved Hide resolved

value_tcx
}

// Returns all the paths that correspond to generated files.
fn generated_output_paths(
tcx: TyCtxt<'_>,
Expand Down Expand Up @@ -632,6 +657,9 @@ pub fn write_dep_info(tcx: TyCtxt<'_>) {
// the side-effect of providing a complete set of all
// accessed files and env vars.
let _ = tcx.resolver_for_lowering();
// Similarly, analysis, codegen and linking should state which environment
// variables they depend on here, as those passes are run after this pass,
// but we'll need the information when emitting dependency info to Cargo.

let sess = tcx.sess;
let _timer = sess.timer("write_dep_info");
Expand Down Expand Up @@ -685,6 +713,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;
proc_macro_decls::provide(providers);
rustc_const_eval::provide(providers);
rustc_middle::hir::provide(providers);
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_lint/src/non_local_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,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_) => {
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_middle/src/query/erase.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::ffi::OsStr;
use std::intrinsics::transmute_unchecked;
use std::mem::MaybeUninit;

Expand Down Expand Up @@ -65,6 +66,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<()>>()];
}
Expand Down Expand Up @@ -180,6 +185,10 @@ impl<T> EraseType for Option<&'_ [T]> {
type Result = [u8; size_of::<Option<&'static [()]>>()];
}

impl EraseType for Option<&'_ OsStr> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably unnecessary.

type Result = [u8; size_of::<Option<&'static OsStr>>()];
}

impl EraseType for Option<mir::DestructuredConstant<'_>> {
type Result = [u8; size_of::<Option<mir::DestructuredConstant<'static>>>()];
}
Expand Down Expand Up @@ -255,6 +264,7 @@ trivial! {
Option<rustc_span::def_id::DefId>,
Option<rustc_span::def_id::LocalDefId>,
Option<rustc_span::Span>,
Option<rustc_span::Symbol>,
Copy link
Contributor

Choose a reason for hiding this comment

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

This is likely not necessary anymore.

Option<rustc_abi::FieldIdx>,
Option<rustc_target::spec::PanicStrategy>,
Option<usize>,
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_middle/src/query/keys.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -492,6 +494,22 @@ 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
}
}

impl<'tcx> Key for Option<&'tcx OsStr> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this necessary?
Option<&'tcx OsStr> doesn't seem to be used as a key.

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> {
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#![allow(unused_parens)]

use std::ffi::OsStr;
use std::mem;
use std::ops::Deref;
use std::path::PathBuf;
Expand Down Expand Up @@ -121,6 +122,21 @@ rustc_queries! {
desc { "perform lints prior to macro expansion" }
}

/// Tracked access to environment variables.
///
/// Useful for the implementation of `std::env!`, `proc-macro`s change
madsmtm marked this conversation as resolved.
Show resolved Hide resolved
/// 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" }
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_middle/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ pub mod call_kind;
pub mod common;
pub mod find_self_call;

use std::env::VarError;
use std::ffi::OsStr;

pub use call_kind::{CallDesugaringKind, CallKind, call_kind};
pub use find_self_call::find_self_call;

use crate::ty::TyCtxt;

#[derive(Default, Copy, Clone)]
pub struct Providers {
pub queries: rustc_middle::query::Providers,
Expand All @@ -29,3 +34,13 @@ impl std::ops::Deref for Providers {
&self.queries
}
}

impl<'tcx> TyCtxt<'tcx> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe put it to compiler\rustc_middle\src\ty\context.rs?
That's where TyCtxt is defined and many of its random inherent methods live.

pub fn env_var(self, key: &'tcx OsStr) -> Result<&'tcx str, VarError> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can use AsRef<OsStr> here, then the function will accept string literals.
You even use key.as_ref() below, which is unnecessary if the key is a non-generic OsStr.

if let Some(value) = self.env_var_os(key.as_ref()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Style nit: if-lets with short (one line) arms typically look better as a match.

value.to_str().ok_or_else(|| VarError::NotUnicode(value.to_os_string()))
} else {
Err(VarError::NotPresent)
}
}
}
18 changes: 18 additions & 0 deletions tests/incremental/env/env_macro.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 18 additions & 0 deletions tests/incremental/env/option_env_macro.rs
Original file line number Diff line number Diff line change
@@ -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());
}