From 91547573af82ddac521a5287e2d03a28454a10ce Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukastw97@gmail.com>
Date: Thu, 29 Feb 2024 14:16:32 +0100
Subject: [PATCH 1/4] Implement `-L builtin:$path`

---
 compiler/rustc_interface/src/tests.rs      | 55 +++++++++++++---------
 compiler/rustc_session/src/config.rs       | 10 ++--
 compiler/rustc_session/src/search_paths.rs | 29 +++++++++++-
 src/librustdoc/config.rs                   |  8 +++-
 4 files changed, 71 insertions(+), 31 deletions(-)

diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 8a27e9a6453b2..8e43d6974fe72 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -315,30 +315,39 @@ fn test_search_paths_tracking_hash_different_order() {
         json_rendered: HumanReadableErrorType::Default(ColorConfig::Never),
     };
 
+    let push = |opts: &mut Options, search_path| {
+        opts.search_paths.push(SearchPath::from_cli_opt(
+            None,
+            &opts.target_triple,
+            &early_dcx,
+            search_path,
+        ));
+    };
+
     // Reference
-    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc"));
-    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def"));
-    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi"));
-    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl"));
-    v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno"));
-
-    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc"));
-    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi"));
-    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def"));
-    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl"));
-    v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno"));
-
-    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def"));
-    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl"));
-    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc"));
-    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi"));
-    v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno"));
-
-    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno"));
-    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc"));
-    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def"));
-    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi"));
-    v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl"));
+    push(&mut v1, "native=abc");
+    push(&mut v1, "crate=def");
+    push(&mut v1, "dependency=ghi");
+    push(&mut v1, "framework=jkl");
+    push(&mut v1, "all=mno");
+
+    push(&mut v2, "native=abc");
+    push(&mut v2, "dependency=ghi");
+    push(&mut v2, "crate=def");
+    push(&mut v2, "framework=jkl");
+    push(&mut v2, "all=mno");
+
+    push(&mut v3, "crate=def");
+    push(&mut v3, "framework=jkl");
+    push(&mut v3, "native=abc");
+    push(&mut v3, "dependency=ghi");
+    push(&mut v3, "all=mno");
+
+    push(&mut v4, "all=mno");
+    push(&mut v4, "native=abc");
+    push(&mut v4, "crate=def");
+    push(&mut v4, "dependency=ghi");
+    push(&mut v4, "framework=jkl");
 
     assert_same_hash(&v1, &v2);
     assert_same_hash(&v1, &v3);
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index c06fe29c5673f..7932865922776 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2795,11 +2795,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     let debuginfo = select_debuginfo(matches, &cg);
     let debuginfo_compression = unstable_opts.debuginfo_compression;
 
-    let mut search_paths = vec![];
-    for s in &matches.opt_strs("L") {
-        search_paths.push(SearchPath::from_cli_opt(early_dcx, s));
-    }
-
     let libs = parse_libs(early_dcx, matches);
 
     let test = matches.opt_present("test");
@@ -2848,6 +2843,11 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
         candidate.join("library/std/src/lib.rs").is_file().then_some(candidate)
     };
 
+    let mut search_paths = vec![];
+    for s in &matches.opt_strs("L") {
+        search_paths.push(SearchPath::from_cli_opt(Some(&sysroot), &target_triple, early_dcx, s));
+    }
+
     let working_dir = std::env::current_dir().unwrap_or_else(|e| {
         early_dcx.early_fatal(format!("Current directory is invalid: {e}"));
     });
diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs
index 32d5e430717bd..58b4a08b5b028 100644
--- a/compiler/rustc_session/src/search_paths.rs
+++ b/compiler/rustc_session/src/search_paths.rs
@@ -1,5 +1,6 @@
 use crate::filesearch::make_target_lib_path;
 use crate::EarlyDiagCtxt;
+use rustc_target::spec::TargetTriple;
 use std::path::{Path, PathBuf};
 
 #[derive(Clone, Debug)]
@@ -46,7 +47,12 @@ impl PathKind {
 }
 
 impl SearchPath {
-    pub fn from_cli_opt(early_dcx: &EarlyDiagCtxt, path: &str) -> Self {
+    pub fn from_cli_opt(
+        sysroot: Option<&Path>,
+        triple: &TargetTriple,
+        early_dcx: &EarlyDiagCtxt,
+        path: &str,
+    ) -> Self {
         let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") {
             (PathKind::Native, stripped)
         } else if let Some(stripped) = path.strip_prefix("crate=") {
@@ -57,6 +63,27 @@ impl SearchPath {
             (PathKind::Framework, stripped)
         } else if let Some(stripped) = path.strip_prefix("all=") {
             (PathKind::All, stripped)
+        } else if let Some(stripped) = path.strip_prefix("builtin:") {
+            let Some(sysroot) = sysroot else {
+                early_dcx.early_fatal("`-L builtin:` is not supported without a sysroot present");
+            };
+            let triple = match triple {
+                TargetTriple::TargetTriple(triple) => triple,
+                TargetTriple::TargetJson { .. } => {
+                    early_dcx.early_fatal("`-L builtin:` is not supported with custom targets");
+                }
+            };
+
+            if stripped.contains(std::path::is_separator) {
+                early_dcx.early_fatal("`-L builtin:` does not accept paths");
+            }
+
+            let path = make_target_lib_path(sysroot, triple).join("builtin").join(stripped);
+            if !path.is_dir() {
+                early_dcx.early_fatal(format!("builtin:{stripped} does not exist"));
+            }
+
+            return Self::new(PathKind::All, path);
         } else {
             (PathKind::All, path)
         };
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index be7e319bc79f4..ab793bb306214 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -460,8 +460,6 @@ impl Options {
             &matches.free[0]
         });
 
-        let libs =
-            matches.opt_strs("L").iter().map(|s| SearchPath::from_cli_opt(early_dcx, s)).collect();
         let externs = parse_externs(early_dcx, matches, &unstable_opts);
         let extern_html_root_urls = match parse_extern_html_roots(matches) {
             Ok(ex) => ex,
@@ -626,6 +624,12 @@ impl Options {
 
         let target = parse_target_triple(early_dcx, matches);
 
+        let libs = matches
+            .opt_strs("L")
+            .iter()
+            .map(|s| SearchPath::from_cli_opt(None, &target, early_dcx, s))
+            .collect();
+
         let show_coverage = matches.opt_present("show-coverage");
 
         let crate_types = match parse_crate_types_from_list(matches.opt_strs("crate-type")) {

From 9ae4e3eb7ca9f9e6c61cedcd8c32f790867a4b37 Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukas.wirth@ferrous-systems.com>
Date: Mon, 4 Mar 2024 09:07:02 +0100
Subject: [PATCH 2/4] Make use of sysroot in librustdoc/config.rs for
 builtin:$path

---
 src/librustdoc/config.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index ab793bb306214..fdbbe62c15889 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -623,11 +623,12 @@ impl Options {
         }
 
         let target = parse_target_triple(early_dcx, matches);
+        let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
 
         let libs = matches
             .opt_strs("L")
             .iter()
-            .map(|s| SearchPath::from_cli_opt(None, &target, early_dcx, s))
+            .map(|s| SearchPath::from_cli_opt(maybe_sysroot.as_deref(), &target, early_dcx, s))
             .collect();
 
         let show_coverage = matches.opt_present("show-coverage");
@@ -657,7 +658,6 @@ impl Options {
         let bin_crate = crate_types.contains(&CrateType::Executable);
         let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
         let playground_url = matches.opt_str("playground-url");
-        let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
         let module_sorting = if matches.opt_present("sort-modules-by-appearance") {
             ModuleSorting::DeclarationOrder
         } else {

From 2fae4ee92ea9a28722673df442112446f7521079 Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukas.wirth@ferrous-systems.com>
Date: Wed, 6 Mar 2024 10:19:34 +0100
Subject: [PATCH 3/4] Make sysroot mandatory for rustdoc

---
 compiler/rustc_interface/src/tests.rs      |  2 +-
 compiler/rustc_session/src/config.rs       |  3 +--
 compiler/rustc_session/src/search_paths.rs | 15 +++------------
 src/librustdoc/config.rs                   |  9 ++++++++-
 4 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 8e43d6974fe72..3b78e6a43ab33 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -317,7 +317,7 @@ fn test_search_paths_tracking_hash_different_order() {
 
     let push = |opts: &mut Options, search_path| {
         opts.search_paths.push(SearchPath::from_cli_opt(
-            None,
+            "not-a-sysroot".as_ref(),
             &opts.target_triple,
             &early_dcx,
             search_path,
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 7932865922776..8d186f7c749e8 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2824,7 +2824,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     let logical_env = parse_logical_env(early_dcx, matches);
 
     let sysroot = filesearch::materialize_sysroot(sysroot_opt);
-
     let real_rust_source_base_dir = {
         // This is the location used by the `rust-src` `rustup` component.
         let mut candidate = sysroot.join("lib/rustlib/src/rust");
@@ -2845,7 +2844,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
     let mut search_paths = vec![];
     for s in &matches.opt_strs("L") {
-        search_paths.push(SearchPath::from_cli_opt(Some(&sysroot), &target_triple, early_dcx, s));
+        search_paths.push(SearchPath::from_cli_opt(&sysroot, &target_triple, early_dcx, s));
     }
 
     let working_dir = std::env::current_dir().unwrap_or_else(|e| {
diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs
index 58b4a08b5b028..9cabdec34ae50 100644
--- a/compiler/rustc_session/src/search_paths.rs
+++ b/compiler/rustc_session/src/search_paths.rs
@@ -48,7 +48,7 @@ impl PathKind {
 
 impl SearchPath {
     pub fn from_cli_opt(
-        sysroot: Option<&Path>,
+        sysroot: &Path,
         triple: &TargetTriple,
         early_dcx: &EarlyDiagCtxt,
         path: &str,
@@ -64,21 +64,12 @@ impl SearchPath {
         } else if let Some(stripped) = path.strip_prefix("all=") {
             (PathKind::All, stripped)
         } else if let Some(stripped) = path.strip_prefix("builtin:") {
-            let Some(sysroot) = sysroot else {
-                early_dcx.early_fatal("`-L builtin:` is not supported without a sysroot present");
-            };
-            let triple = match triple {
-                TargetTriple::TargetTriple(triple) => triple,
-                TargetTriple::TargetJson { .. } => {
-                    early_dcx.early_fatal("`-L builtin:` is not supported with custom targets");
-                }
-            };
-
             if stripped.contains(std::path::is_separator) {
                 early_dcx.early_fatal("`-L builtin:` does not accept paths");
             }
 
-            let path = make_target_lib_path(sysroot, triple).join("builtin").join(stripped);
+            let path =
+                make_target_lib_path(sysroot, triple.triple()).join("builtin").join(stripped);
             if !path.is_dir() {
                 early_dcx.early_fatal(format!("builtin:{stripped} does not exist"));
             }
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index fdbbe62c15889..f078ad2fb5376 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -625,10 +625,17 @@ impl Options {
         let target = parse_target_triple(early_dcx, matches);
         let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
 
+        let sysroot = match &maybe_sysroot {
+            Some(s) => s.clone(),
+            None => {
+                rustc_session::filesearch::get_or_default_sysroot().expect("Failed finding sysroot")
+            }
+        };
+
         let libs = matches
             .opt_strs("L")
             .iter()
-            .map(|s| SearchPath::from_cli_opt(maybe_sysroot.as_deref(), &target, early_dcx, s))
+            .map(|s| SearchPath::from_cli_opt(&sysroot, &target, early_dcx, s))
             .collect();
 
         let show_coverage = matches.opt_present("show-coverage");

From 3d65c920404959cc8ef3ba9e963a31fc91983caf Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukas.wirth@ferrous-systems.com>
Date: Wed, 6 Mar 2024 13:28:12 +0100
Subject: [PATCH 4/4] Replace implementation with @RUSTC_BUILTIN prefix
 substitution var

---
 compiler/rustc_session/src/config.rs       |  1 +
 compiler/rustc_session/src/search_paths.rs | 21 +++++++--------------
 2 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 8d186f7c749e8..f612e8b5b1a55 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2824,6 +2824,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     let logical_env = parse_logical_env(early_dcx, matches);
 
     let sysroot = filesearch::materialize_sysroot(sysroot_opt);
+
     let real_rust_source_base_dir = {
         // This is the location used by the `rust-src` `rustup` component.
         let mut candidate = sysroot.join("lib/rustlib/src/rust");
diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs
index 9cabdec34ae50..16dd40acef047 100644
--- a/compiler/rustc_session/src/search_paths.rs
+++ b/compiler/rustc_session/src/search_paths.rs
@@ -63,27 +63,20 @@ impl SearchPath {
             (PathKind::Framework, stripped)
         } else if let Some(stripped) = path.strip_prefix("all=") {
             (PathKind::All, stripped)
-        } else if let Some(stripped) = path.strip_prefix("builtin:") {
-            if stripped.contains(std::path::is_separator) {
-                early_dcx.early_fatal("`-L builtin:` does not accept paths");
-            }
-
-            let path =
-                make_target_lib_path(sysroot, triple.triple()).join("builtin").join(stripped);
-            if !path.is_dir() {
-                early_dcx.early_fatal(format!("builtin:{stripped} does not exist"));
-            }
-
-            return Self::new(PathKind::All, path);
         } else {
             (PathKind::All, path)
         };
-        if path.is_empty() {
+        let dir = match path.strip_prefix("@RUSTC_BUILTIN") {
+            Some(stripped) => {
+                make_target_lib_path(sysroot, triple.triple()).join("builtin").join(stripped)
+            }
+            None => PathBuf::from(path),
+        };
+        if dir.as_os_str().is_empty() {
             #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
             early_dcx.early_fatal("empty search path given via `-L`");
         }
 
-        let dir = PathBuf::from(path);
         Self::new(kind, dir)
     }