diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs
index 8c2fa6ee95f33..d772642b88b26 100644
--- a/compiler/rustc_builtin_macros/src/env.rs
+++ b/compiler/rustc_builtin_macros/src/env.rs
@@ -13,6 +13,16 @@ use thin_vec::thin_vec;
 
 use crate::errors;
 
+fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Option<Symbol> {
+    let var = var.as_str();
+    if let Some(value) = cx.sess.opts.logical_env.get(var) {
+        return Some(Symbol::intern(value));
+    }
+    // If the environment variable was not defined with the `--env` option, we try to retrieve it
+    // from rustc's environment.
+    env::var(var).ok().as_deref().map(Symbol::intern)
+}
+
 pub fn expand_option_env<'cx>(
     cx: &'cx mut ExtCtxt<'_>,
     sp: Span,
@@ -23,7 +33,7 @@ pub fn expand_option_env<'cx>(
     };
 
     let sp = cx.with_def_site_ctxt(sp);
-    let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern);
+    let value = lookup_env(cx, var);
     cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
     let e = match value {
         None => {
@@ -77,7 +87,7 @@ pub fn expand_env<'cx>(
     };
 
     let span = cx.with_def_site_ctxt(sp);
-    let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern);
+    let value = lookup_env(cx, var);
     cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
     let e = match value {
         None => {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 1ea3ab0d5ecbb..6a8427616e7ac 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -8,7 +8,7 @@ use crate::search_paths::SearchPath;
 use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use crate::{lint, HashStableContext};
 use crate::{EarlyErrorHandler, Session};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
 use rustc_errors::emitter::HumanReadableErrorType;
 use rustc_errors::{ColorConfig, DiagnosticArgValue, HandlerFlags, IntoDiagnosticArg};
@@ -1114,6 +1114,7 @@ impl Default for Options {
             pretty: None,
             working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
             color: ColorConfig::Auto,
+            logical_env: FxIndexMap::default(),
         }
     }
 }
@@ -1810,6 +1811,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
             "Remap source names in all output (compiler messages and output files)",
             "FROM=TO",
         ),
+        opt::multi("", "env", "Inject an environment variable", "VAR=VALUE"),
     ]);
     opts
 }
@@ -2589,6 +2591,23 @@ fn parse_remap_path_prefix(
     mapping
 }
 
+fn parse_logical_env(
+    handler: &mut EarlyErrorHandler,
+    matches: &getopts::Matches,
+) -> FxIndexMap<String, String> {
+    let mut vars = FxIndexMap::default();
+
+    for arg in matches.opt_strs("env") {
+        if let Some((name, val)) = arg.split_once('=') {
+            vars.insert(name.to_string(), val.to_string());
+        } else {
+            handler.early_error(format!("`--env`: specify value for variable `{arg}`"));
+        }
+    }
+
+    vars
+}
+
 // JUSTIFICATION: before wrapper fn is available
 #[allow(rustc::bad_opt_access)]
 pub fn build_session_options(
@@ -2827,6 +2846,8 @@ pub fn build_session_options(
         handler.early_error("can't dump dependency graph without `-Z query-dep-graph`");
     }
 
+    let logical_env = parse_logical_env(handler, matches);
+
     // Try to find a directory containing the Rust `src`, for more details see
     // the doc comment on the `real_rust_source_base_dir` field.
     let tmp_buf;
@@ -2907,6 +2928,7 @@ pub fn build_session_options(
         pretty,
         working_dir,
         color,
+        logical_env,
     }
 }
 
@@ -3181,6 +3203,7 @@ pub(crate) mod dep_tracking {
     };
     use crate::lint;
     use crate::utils::NativeLib;
+    use rustc_data_structures::fx::FxIndexMap;
     use rustc_data_structures::stable_hasher::Hash64;
     use rustc_errors::LanguageIdentifier;
     use rustc_feature::UnstableFeatures;
@@ -3339,6 +3362,21 @@ pub(crate) mod dep_tracking {
         }
     }
 
+    impl<T: DepTrackingHash, V: DepTrackingHash> DepTrackingHash for FxIndexMap<T, V> {
+        fn hash(
+            &self,
+            hasher: &mut DefaultHasher,
+            error_format: ErrorOutputType,
+            for_crate_hash: bool,
+        ) {
+            Hash::hash(&self.len(), hasher);
+            for (key, value) in self.iter() {
+                DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
+                DepTrackingHash::hash(value, hasher, error_format, for_crate_hash);
+            }
+        }
+    }
+
     impl DepTrackingHash for OutputTypes {
         fn hash(
             &self,
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 7a6108bfbe24c..982cbe3bd9514 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -3,6 +3,7 @@ use crate::config::*;
 use crate::search_paths::SearchPath;
 use crate::utils::NativeLib;
 use crate::{lint, EarlyErrorHandler};
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::profiling::TimePassesFormat;
 use rustc_data_structures::stable_hasher::Hash64;
 use rustc_errors::ColorConfig;
@@ -150,6 +151,9 @@ top_level_options!(
 
         target_triple: TargetTriple [TRACKED],
 
+        /// Effective logical environment used by `env!`/`option_env!` macros
+        logical_env: FxIndexMap<String, String> [TRACKED],
+
         test: bool [TRACKED],
         error_format: ErrorOutputType [UNTRACKED],
         diagnostic_width: Option<usize> [UNTRACKED],
diff --git a/src/doc/unstable-book/src/compiler-flags/env.md b/src/doc/unstable-book/src/compiler-flags/env.md
new file mode 100644
index 0000000000000..df0547dd24b6c
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/env.md
@@ -0,0 +1,26 @@
+# `env`
+
+The tracking issue for this feature is: [#118372](https://github.com/rust-lang/rust/issues/118372).
+
+------------------------
+
+This option flag allows to specify environment variables value at compile time to be
+used by `env!` and `option_env!` macros.
+
+When retrieving an environment variable value, the one specified by `--env` will take
+precedence. For example, if you want have `PATH=a` in your environment and pass:
+
+```bash
+rustc --env PATH=env
+```
+
+Then you will have:
+
+```rust,no_run
+assert_eq!(env!("PATH"), "env");
+```
+
+Please note that on Windows, environment variables are case insensitive but case
+preserving whereas `rustc`'s environment variables are case sensitive. For example,
+having `Path` in your environment (case insensitive) is different than using
+`rustc --env Path=...` (case sensitive).
diff --git a/tests/ui/extenv/extenv-env-overload.rs b/tests/ui/extenv/extenv-env-overload.rs
new file mode 100644
index 0000000000000..b82bb2fe9661a
--- /dev/null
+++ b/tests/ui/extenv/extenv-env-overload.rs
@@ -0,0 +1,9 @@
+// run-pass
+// rustc-env:MY_VAR=tadam
+// compile-flags: --env MY_VAR=123abc -Zunstable-options
+
+// This test ensures that variables provided with `--env` take precedence over
+// variables from environment.
+fn main() {
+    assert_eq!(env!("MY_VAR"), "123abc");
+}
diff --git a/tests/ui/extenv/extenv-env.rs b/tests/ui/extenv/extenv-env.rs
new file mode 100644
index 0000000000000..9fda52b894111
--- /dev/null
+++ b/tests/ui/extenv/extenv-env.rs
@@ -0,0 +1,5 @@
+// compile-flags: --env FOO=123abc -Zunstable-options
+// run-pass
+fn main() {
+    assert_eq!(env!("FOO"), "123abc");
+}
diff --git a/tests/ui/extenv/extenv-not-env.rs b/tests/ui/extenv/extenv-not-env.rs
new file mode 100644
index 0000000000000..d6c4a43b0032a
--- /dev/null
+++ b/tests/ui/extenv/extenv-not-env.rs
@@ -0,0 +1,7 @@
+// run-pass
+// rustc-env:MY_ENV=/
+// Ensures that variables not defined through `--env` are still available.
+
+fn main() {
+    assert!(!env!("MY_ENV").is_empty());
+}
diff --git a/tests/ui/feature-gates/env-flag.rs b/tests/ui/feature-gates/env-flag.rs
new file mode 100644
index 0000000000000..9dfda2584fbc2
--- /dev/null
+++ b/tests/ui/feature-gates/env-flag.rs
@@ -0,0 +1,3 @@
+// compile-flags: --env A=B
+
+fn main() {}
diff --git a/tests/ui/feature-gates/env-flag.stderr b/tests/ui/feature-gates/env-flag.stderr
new file mode 100644
index 0000000000000..5cb18cef9fbc2
--- /dev/null
+++ b/tests/ui/feature-gates/env-flag.stderr
@@ -0,0 +1,2 @@
+error: the `-Z unstable-options` flag must also be passed to enable the flag `env`
+