diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 932a9bdd56a..9d9d732aca6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -296,7 +296,7 @@ jobs: # Check with all compatible features cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-types --no-default-features --features strict_asserts,fragile-send-sync-non-atomic-wasm,serde,counters - cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p naga --no-default-features --features dot-out + cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p naga --no-default-features --features dot-out,spv-in,spv-out cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu-hal --no-default-features --features fragile-send-sync-non-atomic-wasm cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} -p wgpu --no-default-features --features serde diff --git a/naga-cli/Cargo.toml b/naga-cli/Cargo.toml index 1ffadd64f8d..16e78631f0b 100644 --- a/naga-cli/Cargo.toml +++ b/naga-cli/Cargo.toml @@ -38,6 +38,7 @@ naga = { workspace = true, features = [ "deserialize", "termcolor", "stderr", + "fs", ] } bincode.workspace = true diff --git a/naga-cli/src/bin/naga.rs b/naga-cli/src/bin/naga.rs index a1e2b4a0bd1..c2104af18a0 100644 --- a/naga-cli/src/bin/naga.rs +++ b/naga-cli/src/bin/naga.rs @@ -424,7 +424,7 @@ fn run() -> anyhow::Result<()> { params.spv_in = naga::front::spv::Options { adjust_coordinate_space: !args.keep_coordinate_space, strict_capabilities: false, - block_ctx_dump_prefix: args.block_ctx_dir.clone().map(std::path::PathBuf::from), + block_ctx_dump_prefix: args.block_ctx_dir.clone().map(Into::into), }; params.entry_point.clone_from(&args.entry_point); @@ -486,7 +486,7 @@ fn run() -> anyhow::Result<()> { .set(naga::back::spv::WriterFlags::DEBUG, true); params.spv_out.debug_info = Some(naga::back::spv::DebugInfo { source_code: input_text, - file_name: input_path, + file_name: input_path.into(), language, }) } else { diff --git a/naga/Cargo.toml b/naga/Cargo.toml index 5dc433890c2..02eda4c198a 100644 --- a/naga/Cargo.toml +++ b/naga/Cargo.toml @@ -23,7 +23,6 @@ default = [] dot-out = [] glsl-in = ["dep:pp-rs"] glsl-out = [] -std = [] ## Enables outputting to the Metal Shading Language (MSL). ## @@ -79,6 +78,9 @@ termcolor = ["codespan-reporting/termcolor"] ## Enables writing output to stderr. stderr = ["codespan-reporting/std"] +## Enables integration with the underlying filesystem. +fs = [] + [dependencies] arbitrary = { workspace = true, features = ["derive"], optional = true } arrayvec.workspace = true diff --git a/naga/build.rs b/naga/build.rs index 38b1a28a869..b2a6d0b8c98 100644 --- a/naga/build.rs +++ b/naga/build.rs @@ -6,7 +6,7 @@ fn main() { msl_out: { any(feature = "msl-out", all(target_vendor = "apple", feature = "msl-out-if-target-apple")) }, spv_out: { feature = "spv-out" }, wgsl_out: { feature = "wgsl-out" }, - std: { any(test, spv_out, feature = "spv-in", feature = "wgsl-in", feature = "stderr") }, + std: { any(test, feature = "wgsl-in", feature = "stderr", feature = "fs") }, no_std: { not(std) }, } } diff --git a/naga/src/as_diagnostic_file_path.rs b/naga/src/as_diagnostic_file_path.rs deleted file mode 100644 index ac3a119c9cc..00000000000 --- a/naga/src/as_diagnostic_file_path.rs +++ /dev/null @@ -1,79 +0,0 @@ -//! [`AsDiagnosticFilePath`] and its supporting items. - -use alloc::borrow::Cow; - -#[cfg(feature = "std")] -use std::path::Path; - -#[cfg(not(feature = "std"))] -use alloc::string::String; - -mod sealed { - pub trait Sealed {} -} - -/// A trait that abstracts over types accepted for conversion to the most -/// featureful path representation possible; that is: -/// -/// - When `no_std` is active, this is implemented for [`String`], [`str`], and [`Cow`] (i.e., -/// `Cow<'_, str>`). -/// - Otherwise, types that implement `AsRef` (to extract a `&Path`). -/// -/// This type is used as the type bounds for various diagnostic rendering methods, i.e., -/// [`WithSpan::emit_to_string_with_path`](crate::span::WithSpan::emit_to_string_with_path). -/// -/// [`String`]: alloc::string::String -pub trait AsDiagnosticFilePath: sealed::Sealed { - fn to_string_lossy(&self) -> Cow<'_, str>; -} - -#[cfg(feature = "std")] -impl + ?Sized> AsDiagnosticFilePath for T { - fn to_string_lossy(&self) -> Cow<'_, str> { - self.as_ref().to_string_lossy() - } -} - -#[cfg(feature = "std")] -impl + ?Sized> sealed::Sealed for T {} - -#[cfg(not(feature = "std"))] -impl AsDiagnosticFilePath for String { - fn to_string_lossy(&self) -> Cow<'_, str> { - Cow::Borrowed(self.as_str()) - } -} - -#[cfg(not(feature = "std"))] -impl sealed::Sealed for String {} - -#[cfg(not(feature = "std"))] -impl AsDiagnosticFilePath for str { - fn to_string_lossy(&self) -> Cow<'_, str> { - Cow::Borrowed(self) - } -} - -#[cfg(not(feature = "std"))] -impl sealed::Sealed for str {} - -#[cfg(not(feature = "std"))] -impl AsDiagnosticFilePath for Cow<'_, str> { - fn to_string_lossy(&self) -> Cow<'_, str> { - use core::borrow::Borrow; - Cow::Borrowed(self.borrow()) - } -} - -#[cfg(not(feature = "std"))] -impl sealed::Sealed for Cow<'_, str> {} - -#[cfg(not(feature = "std"))] -impl AsDiagnosticFilePath for &T { - fn to_string_lossy(&self) -> Cow<'_, str> { - (*self).to_string_lossy() - } -} - -#[cfg(not(feature = "std"))] -impl sealed::Sealed for &T {} diff --git a/naga/src/back/spv/mod.rs b/naga/src/back/spv/mod.rs index 986bee57d31..2dcd95957d7 100644 --- a/naga/src/back/spv/mod.rs +++ b/naga/src/back/spv/mod.rs @@ -25,6 +25,7 @@ use spirv::Word; use thiserror::Error; use crate::arena::{Handle, HandleVec}; +use crate::path_like::PathLikeRef; use crate::proc::{BoundsCheckPolicies, TypeResolution}; #[derive(Clone)] @@ -92,7 +93,7 @@ impl IdGenerator { #[derive(Debug, Clone)] pub struct DebugInfo<'a> { pub source_code: &'a str, - pub file_name: &'a std::path::Path, + pub file_name: PathLikeRef<'a>, pub language: SourceLanguage, } diff --git a/naga/src/back/spv/writer.rs b/naga/src/back/spv/writer.rs index 9f4422345b5..b61747c8326 100644 --- a/naga/src/back/spv/writer.rs +++ b/naga/src/back/spv/writer.rs @@ -1,8 +1,4 @@ -use alloc::{ - string::{String, ToString}, - vec, - vec::Vec, -}; +use alloc::{string::String, vec, vec::Vec}; use hashbrown::hash_map::Entry; use spirv::Word; @@ -18,6 +14,7 @@ use super::{ use crate::{ arena::{Handle, HandleVec, UniqueArena}, back::spv::{BindingInfo, WrappedFunction}, + path_like::PathLike, proc::{Alignment, TypeResolution}, valid::{FunctionInfo, ModuleInfo}, }; @@ -2415,7 +2412,7 @@ impl Writer { if let Some(debug_info) = debug_info.as_ref() { let source_file_id = self.id_gen.next(); self.debugs.push(Instruction::string( - &debug_info.file_name.display().to_string(), + &debug_info.file_name.to_string_lossy(), source_file_id, )); diff --git a/naga/src/front/spv/function.rs b/naga/src/front/spv/function.rs index 9c59d0a8506..13163f6bd41 100644 --- a/naga/src/front/spv/function.rs +++ b/naga/src/front/spv/function.rs @@ -169,10 +169,18 @@ impl> super::Frontend { Some(ep) => format!("block_ctx.{:?}-{}.txt", ep.stage, ep.name), None => format!("block_ctx.Fun-{}.txt", function_index), }; - let dest = prefix.join(dump_suffix); - let dump = format!("{block_ctx:#?}"); - if let Err(e) = std::fs::write(&dest, dump) { - log::error!("Unable to dump the block context into {:?}: {}", dest, e); + + cfg_if::cfg_if! { + if #[cfg(feature = "fs")] { + let prefix: &std::path::Path = prefix.as_ref(); + let dest = prefix.join(dump_suffix); + let dump = format!("{block_ctx:#?}"); + if let Err(e) = std::fs::write(&dest, dump) { + log::error!("Unable to dump the block context into {:?}: {}", dest, e); + } + } else { + log::error!("Unable to dump the block context into {:?}/{}: file system integration was not enabled with the `fs` feature", prefix, dump_suffix); + } } } diff --git a/naga/src/front/spv/mod.rs b/naga/src/front/spv/mod.rs index 7e4f6c9be11..2375e61c531 100644 --- a/naga/src/front/spv/mod.rs +++ b/naga/src/front/spv/mod.rs @@ -37,7 +37,6 @@ pub use error::Error; use alloc::{borrow::ToOwned, format, string::String, vec, vec::Vec}; use core::{convert::TryInto, mem, num::NonZeroU32}; -use std::path::PathBuf; use half::f16; use petgraph::graphmap::GraphMap; @@ -45,6 +44,7 @@ use petgraph::graphmap::GraphMap; use super::atomic_upgrade::Upgrades; use crate::{ arena::{Arena, Handle, UniqueArena}, + path_like::PathLikeOwned, proc::{Alignment, Layouter}, FastHashMap, FastHashSet, FastIndexMap, }; @@ -381,7 +381,7 @@ pub struct Options { pub adjust_coordinate_space: bool, /// Only allow shaders with the known set of capabilities. pub strict_capabilities: bool, - pub block_ctx_dump_prefix: Option, + pub block_ctx_dump_prefix: Option, } impl Default for Options { diff --git a/naga/src/lib.rs b/naga/src/lib.rs index 731b2bc2d61..bdd5345c312 100644 --- a/naga/src/lib.rs +++ b/naga/src/lib.rs @@ -106,7 +106,6 @@ extern crate std; extern crate alloc; mod arena; -mod as_diagnostic_file_path; pub mod back; pub mod common; pub mod compact; @@ -116,6 +115,7 @@ pub mod front; pub mod ir; pub mod keywords; mod non_max_u32; +mod path_like; pub mod proc; mod racy_lock; mod span; diff --git a/naga/src/path_like.rs b/naga/src/path_like.rs new file mode 100644 index 00000000000..ef6519238f3 --- /dev/null +++ b/naga/src/path_like.rs @@ -0,0 +1,187 @@ +//! [`PathLike`] and its supporting items, such as [`PathLikeRef`] and [`PathLikeOwned`]. + +use alloc::borrow::Cow; +use core::fmt; + +mod sealed { + pub trait Sealed {} +} + +/// A trait that abstracts over types accepted for conversion to the most +/// featureful path representation possible; that is: +/// +/// - When `no_std` is active, this is implemented for [`String`], [`str`], and [`Cow`] (i.e., +/// `Cow<'_, str>`). +/// - Otherwise, types that implement `AsRef` (to extract a `&Path`). +/// +/// This type is used as the type bounds for various diagnostic rendering methods, i.e., +/// [`WithSpan::emit_to_string_with_path`](crate::span::WithSpan::emit_to_string_with_path). +/// +/// [`String`]: alloc::string::String +pub trait PathLike: sealed::Sealed { + fn to_string_lossy(&self) -> Cow<'_, str>; +} + +/// Abstraction over `Path` which falls back to [`str`] for `no_std` compatibility. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PathLikeRef<'a>(&'a impls::PathInner); + +impl fmt::Debug for PathLikeRef<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.0, f) + } +} + +/// Abstraction over `PathBuf` which falls back to [`String`] for `no_std` compatibility. +/// +/// [`String`]: alloc::string::String +#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PathLikeOwned(::Owned); + +impl fmt::Debug for PathLikeOwned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +#[cfg(std)] +mod impls { + use alloc::{borrow::Cow, string::String}; + use std::path::{Path, PathBuf}; + + use super::{sealed, PathLike, PathLikeOwned, PathLikeRef}; + + pub(super) type PathInner = Path; + + impl + ?Sized> PathLike for T { + fn to_string_lossy(&self) -> Cow<'_, str> { + self.as_ref().to_string_lossy() + } + } + + impl + ?Sized> sealed::Sealed for T {} + + impl AsRef for PathLikeRef<'_> { + fn as_ref(&self) -> &Path { + self.0 + } + } + + impl AsRef for PathLikeOwned { + fn as_ref(&self) -> &Path { + self.0.as_ref() + } + } + + impl<'a> From<&'a str> for PathLikeRef<'a> { + fn from(value: &'a str) -> Self { + Self(Path::new(value)) + } + } + + impl<'a> From<&'a Path> for PathLikeRef<'a> { + fn from(value: &'a Path) -> Self { + Self(value) + } + } + + impl<'a> From> for &'a Path { + fn from(value: PathLikeRef<'a>) -> Self { + value.0 + } + } + + impl From for PathLikeOwned { + fn from(value: String) -> Self { + Self(PathBuf::from(value)) + } + } + + impl From for PathLikeOwned { + fn from(value: PathBuf) -> Self { + Self(value) + } + } + + impl From for PathBuf { + fn from(value: PathLikeOwned) -> Self { + value.0 + } + } + + impl AsRef for PathLikeOwned { + fn as_ref(&self) -> &PathBuf { + &self.0 + } + } +} + +#[cfg(no_std)] +mod impls { + use alloc::{borrow::Cow, string::String}; + use core::borrow::Borrow; + + use super::{sealed, PathLike, PathLikeOwned, PathLikeRef}; + + pub(super) type PathInner = str; + + impl PathLike for String { + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self.as_str()) + } + } + + impl sealed::Sealed for String {} + + impl PathLike for str { + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self) + } + } + + impl sealed::Sealed for str {} + + impl PathLike for Cow<'_, str> { + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self.borrow()) + } + } + + impl sealed::Sealed for Cow<'_, str> {} + + impl PathLike for &T { + fn to_string_lossy(&self) -> Cow<'_, str> { + (*self).to_string_lossy() + } + } + + impl sealed::Sealed for &T {} + + impl PathLike for PathLikeRef<'_> { + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self.0) + } + } + + impl sealed::Sealed for PathLikeRef<'_> {} + + impl PathLike for PathLikeOwned { + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self.0.borrow()) + } + } + + impl sealed::Sealed for PathLikeOwned {} + + impl<'a> From<&'a str> for PathLikeRef<'a> { + fn from(value: &'a str) -> Self { + Self(value) + } + } + + impl From for PathLikeOwned { + fn from(value: String) -> Self { + Self(value) + } + } +} diff --git a/naga/src/span.rs b/naga/src/span.rs index 4d6e0352800..942846e46b5 100644 --- a/naga/src/span.rs +++ b/naga/src/span.rs @@ -6,7 +6,7 @@ use alloc::{ }; use core::{error::Error, fmt, ops::Range}; -use crate::{as_diagnostic_file_path::AsDiagnosticFilePath, Arena, Handle, UniqueArena}; +use crate::{path_like::PathLike, Arena, Handle, UniqueArena}; /// A source code span, used for error reporting. #[derive(Clone, Copy, Debug, PartialEq, Default)] @@ -286,7 +286,7 @@ impl WithSpan { pub fn emit_to_stderr_with_path

(&self, source: &str, path: P) where E: Error, - P: AsDiagnosticFilePath, + P: PathLike, { use codespan_reporting::{files, term}; @@ -318,7 +318,7 @@ impl WithSpan { pub fn emit_to_string_with_path

(&self, source: &str, path: P) -> String where E: Error, - P: AsDiagnosticFilePath, + P: PathLike, { use codespan_reporting::{files, term}; diff --git a/naga/tests/naga/snapshots.rs b/naga/tests/naga/snapshots.rs index 08a398af30d..32e2f5e0285 100644 --- a/naga/tests/naga/snapshots.rs +++ b/naga/tests/naga/snapshots.rs @@ -476,7 +476,7 @@ fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<& if let Some(source_code) = source_code { debug_info = Some(naga::back::spv::DebugInfo { source_code, - file_name: name, + file_name: name.as_path().into(), // wgpu#6266: we technically know all the information here to // produce the valid language but it's not too important for // validation purposes @@ -883,7 +883,7 @@ fn convert_snapshots_spv() { &naga::front::spv::Options { adjust_coordinate_space, strict_capabilities: true, - block_ctx_dump_prefix: None, + ..Default::default() }, ) .unwrap(); diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 77b9ebe9244..f4f68d86275 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -885,7 +885,7 @@ impl super::Device { if let Some(ref debug) = naga_shader.debug_source { temp_options.debug_info = Some(naga::back::spv::DebugInfo { source_code: &debug.source_code, - file_name: debug.file_name.as_ref().as_ref(), + file_name: debug.file_name.as_ref().into(), language: naga::back::spv::SourceLanguage::WGSL, }) } @@ -1884,7 +1884,7 @@ impl crate::Device for super::Device { .as_ref() .map(|d| naga::back::spv::DebugInfo { source_code: d.source_code.as_ref(), - file_name: d.file_name.as_ref().as_ref(), + file_name: d.file_name.as_ref().into(), language: naga::back::spv::SourceLanguage::WGSL, }); if !desc.runtime_checks.bounds_checks {