Refactor label conversion to handle source dirs that aren't labels as well.

Bug: 528225104
Change-Id: I325db854da2020842930a272f566f04e6a6a6964
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/23620
Reviewed-by: Takuto Ikuta <tikuta@google.com>
Commit-Queue: Matt Stark <msta@google.com>
diff --git a/build/gen.py b/build/gen.py
index 392249f..c539995 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -631,6 +631,9 @@
         '/wd4838',
         '/wd4996',
         '/std:c++23preview',
+        # Enable __cplusplus macro to report the correct C++ standard version,
+        # otherwise it defaults to C++98.
+        '/Zc:__cplusplus',
         '/GR-',
         '/D_HAS_EXCEPTIONS=0',
     ])
@@ -1011,7 +1014,6 @@
           '-lshlwapi',
       ])
 
-
   libs.extend(options.link_libs)
 
   # we just build static libraries that GN needs
diff --git a/src/gn/ffi/label.cc b/src/gn/ffi/label.cc
index c0ddbdb..3818d4c 100644
--- a/src/gn/ffi/label.cc
+++ b/src/gn/ffi/label.cc
@@ -7,12 +7,16 @@
 
 extern "C" {
 
-rust::Str GetLabelDir(const Label& label) {
-  return label.dir().value();
+const SourceDir& GetLabelDir(const Label& label) {
+  return label.dir();
 }
 
 rust::Str GetLabelName(const Label& label) {
   return label.name();
 }
 
+rust::Str GetSourceDirValue(const SourceDir& dir) {
+  return dir.SourceWithNoTrailingSlash();
+}
+
 }  // extern "C"
diff --git a/src/gn/starlark/crates/ffi/src/label.rs b/src/gn/starlark/crates/ffi/src/label.rs
index b1eedf6..df3c42b 100644
--- a/src/gn/starlark/crates/ffi/src/label.rs
+++ b/src/gn/starlark/crates/ffi/src/label.rs
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use crate::declare_opaque_type;
 use types::{LabelRef, PackageRef};
 
+use crate::declare_opaque_type;
 declare_opaque_type!(pub Label);
 
 impl Label {
     /// Returns the directory part of the label (the package path).
     pub fn package(&self) -> &PackageRef {
         extern "C" {
-            fn GetLabelDir(label: &Label) -> &str;
+            fn GetLabelDir(label: &Label) -> &SourceDir;
         }
-        // Safety: GetLabelDir is guarunteed to return a string that starts with "//".
-        unsafe { PackageRef::new_unchecked(GetLabelDir(self)) }
+        // Safety: Just an FFI function.
+        unsafe { GetLabelDir(self) }.as_rust()
     }
 
     /// Returns the name part of the label.
@@ -22,11 +22,31 @@
         extern "C" {
             fn GetLabelName(label: &Label) -> &str;
         }
+        // Safety: Just an FFI function.
         unsafe { GetLabelName(self) }
     }
 
     /// Returns a `LabelRef` referencing the directory and name of this label.
-    pub fn as_ref<'a>(&'a self) -> LabelRef<'a> {
+    pub fn as_ref(&self) -> LabelRef<'_> {
         LabelRef::new(self.package(), self.name())
     }
 }
+
+declare_opaque_type!(pub SourceDir);
+
+impl SourceDir {
+    pub fn as_rust(&self) -> &types::PackageRef {
+        extern "C" {
+            fn GetSourceDirValue(dir: &SourceDir) -> &str;
+        }
+        // Safety: Just an FFI function.
+        let s = unsafe { GetSourceDirValue(self) };
+        // While source dirs aren't guarunteed to start with "//" (they may be
+        // absolute), we only convert source dirs to rust for either labels or
+        // BUILD.gn directories, both of which are guarunteed to be
+        // source-relative.
+        debug_assert!(s.starts_with("//"));
+        // Safety: Guarunteed to be a valid package.
+        unsafe { types::PackageRef::new_unchecked(s) }
+    }
+}
diff --git a/src/gn/starlark/crates/ffi/src/lib.rs b/src/gn/starlark/crates/ffi/src/lib.rs
index 15f108e..f4abc9c 100644
--- a/src/gn/starlark/crates/ffi/src/lib.rs
+++ b/src/gn/starlark/crates/ffi/src/lib.rs
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-//! Low-level FFI bindings and opaque types for interoperating between the C++ GN
-//! codebase and the Rust Starlark interpreter crates.
+//! Low-level FFI bindings and opaque types for interoperating between the C++
+//! GN codebase and the Rust Starlark interpreter crates.
 //!
 //! **Type Equivalence**
 //!
@@ -11,14 +11,18 @@
 //! rust type signatures. This means that if you change the signature of a
 //! function on one side, you *might* get a link error, or might just get UB.
 //!
-//! As long as C++ and rust see a type identically, we don't care how it works under the hood.
+//! As long as C++ and rust see a type identically, we don't care how it works
+//! under the hood.
 //! * Most custom types will need to be passed by reference
 //!   * C++ &T is just a non-null pointer.
 //!   * Rust references are just non-null pointers
 //!   * Rust Option<&T> are pointers
-//! * If you define a struct in C++ and a #[repr(C)] one in rust, you can pass by value instead of by reference.
-//!   * cxx.h does this for some rust types in C++ (rust::*, eg. rust::Str = &str)
-//!   * cxx.h does this for some C++ types in rust (cxx::*, eg. cxx::CxxVector<T> = std::vector<T>)
+//! * If you define a struct in C++ and a #[repr(C)] one in rust, you can pass
+//!   by value instead of by reference.
+//!   * cxx.h does this for some rust types in C++ (rust::*, eg. rust::Str =
+//!     &str)
+//!   * cxx.h does this for some C++ types in rust (cxx::*, eg.
+//!     cxx::CxxVector<T> = std::vector<T>)
 //!
 //! **Exposing rust functions to C++**
 //!
@@ -62,4 +66,4 @@
 pub mod label;
 pub mod opaque;
 
-pub use label::Label;
+pub use label::{Label, SourceDir};