[naga wgsl] Fix generics on naga::common::wgsl::TypeContext.

In `naga::common::wgsl::types`, move the type parameter `<W: Write>`
from the `TypeContext` trait itself to the individual methods of the
trait. It is very unlikely for Naga to need to be able to implement
`TypeContext`, but only for one particular kind of output stream.

The motivation for this change is that the old parameterization makes
it awkward to provide utility methods for generating `String`s on the
trait, which we do in subsequent commits. In order to write to a
`String`, such utility methods need `Self` to implement
`TypeContext<String>`, so you can add bounds to the methods like this:

    fn type_to_string(..., out: &mut W) -> ...
        where Self: TypeContext<String>
    {
        ... self.write(..., &mut string_buf)?;
    }

That will compile, but if you try to actually call it, Rust gets
confused. Remember, the above is not a method on
`TypeContext<String>`, it's a method on `TypeContext<W>` that uses
`TypeContext<String>` internally. So when you write
`ctx.type_to_string(...)`, Rust needs to decide whether `ctx`
implements `TypeContext<W>` for some completely unspecified `W`, and
asks for type annotations.

You could supply type annotations, but this would be basically
supplying some never-used type that implements `core::fmt::Write`.
Instead of

    ctx.type_to_string(handle)

you'd have to write

    TypeContext::<String>::type_to_string(ctx, handle)

which is dumb.

(I don't *think* this explanation belongs in the code, since it's an
explanation of a design *not* used, replaced by a design that's pretty
natural --- so I'll leave it here.)
This commit is contained in:
Jim Blandy
2025-03-16 12:35:03 -07:00
committed by Erich Gubler
parent fc6d6c6483
commit e17c0bdc17
2 changed files with 21 additions and 14 deletions

View File

@@ -1726,7 +1726,7 @@ struct WriterTypeContext<'m> {
names: &'m crate::FastHashMap<NameKey, String>,
}
impl<W: Write> TypeContext<W> for WriterTypeContext<'_> {
impl TypeContext for WriterTypeContext<'_> {
fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {
&self.module.types[handle]
}
@@ -1735,15 +1735,15 @@ impl<W: Write> TypeContext<W> for WriterTypeContext<'_> {
self.names[&NameKey::Type(handle)].as_str()
}
fn write_override(&self, _: Handle<crate::Override>, _: &mut W) -> core::fmt::Result {
fn write_override<W: Write>(&self, _: Handle<crate::Override>, _: &mut W) -> core::fmt::Result {
unreachable!("overrides should be validated out");
}
fn write_non_wgsl_inner(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
fn write_non_wgsl_inner<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
unreachable!("backends should only be passed validated modules");
}
fn write_non_wgsl_scalar(&self, _: crate::Scalar, _: &mut W) -> core::fmt::Result {
fn write_non_wgsl_scalar<W: Write>(&self, _: crate::Scalar, _: &mut W) -> core::fmt::Result {
unreachable!("backends should only be passed validated modules");
}
}

View File

@@ -21,7 +21,7 @@ use core::fmt::Write;
/// [`write_type`]: TypeContext::write_type
/// [`write_type_inner`]: TypeContext::write_type_inner
/// [`type_name`]: TypeContext::type_name
pub trait TypeContext<W: Write> {
pub trait TypeContext {
/// Return the [`Type`] referred to by `handle`.
///
/// [`Type`]: crate::Type
@@ -32,8 +32,11 @@ pub trait TypeContext<W: Write> {
fn type_name(&self, handle: Handle<crate::Type>) -> &str;
/// Write the WGSL form of `override` to `out`.
fn write_override(&self, r#override: Handle<crate::Override>, out: &mut W)
-> core::fmt::Result;
fn write_override<W: Write>(
&self,
r#override: Handle<crate::Override>,
out: &mut W,
) -> core::fmt::Result;
/// Write a [`TypeInner`] that has no representation as WGSL source,
/// even including Naga extensions.
@@ -43,7 +46,7 @@ pub trait TypeContext<W: Write> {
/// validation, whereas something generating type names to appear in error messages
/// might punt to `TypeInner`'s [`core::fmt::Debug`] implementation, since it's
/// probably better to show the user something they can act on.
fn write_non_wgsl_inner(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result;
fn write_non_wgsl_inner<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result;
/// Write a [`Scalar`] that has no representation as WGSL source,
/// even including Naga extensions.
@@ -53,13 +56,13 @@ pub trait TypeContext<W: Write> {
/// validation, whereas something generating type names to appear in error messages
/// might punt to `Scalar`'s [`core::fmt::Debug`] implementation, since it's
/// probably better to show the user something they can act on.
fn write_non_wgsl_scalar(&self, scalar: Scalar, out: &mut W) -> core::fmt::Result;
fn write_non_wgsl_scalar<W: Write>(&self, scalar: Scalar, out: &mut W) -> core::fmt::Result;
/// Write the type `ty` as it would appear in a value's declaration.
///
/// Write the type referred to by `ty` in `module` as it would appear in
/// a `var`, `let`, etc. declaration, or in a function's argument list.
fn write_type(&self, handle: Handle<crate::Type>, out: &mut W) -> core::fmt::Result {
fn write_type<W: Write>(&self, handle: Handle<crate::Type>, out: &mut W) -> core::fmt::Result {
let ty = self.lookup_type(handle);
match ty.inner {
TypeInner::Struct { .. } => out.write_str(self.type_name(handle))?,
@@ -79,7 +82,7 @@ pub trait TypeContext<W: Write> {
/// [`TypeInner`].
///
/// [`Struct`]: TypeInner::Struct
fn write_type_inner(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result {
fn write_type_inner<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result {
match try_write_type_inner(self, inner, out) {
Ok(()) => Ok(()),
Err(WriteTypeError::Format(err)) => Err(err),
@@ -88,7 +91,7 @@ pub trait TypeContext<W: Write> {
}
/// Write the [`Scalar`] `scalar` as a WGSL type.
fn write_scalar(&self, scalar: Scalar, out: &mut W) -> core::fmt::Result {
fn write_scalar<W: Write>(&self, scalar: Scalar, out: &mut W) -> core::fmt::Result {
match scalar.try_to_wgsl() {
Some(string) => out.write_str(string),
None => self.write_non_wgsl_scalar(scalar, out),
@@ -96,7 +99,11 @@ pub trait TypeContext<W: Write> {
}
/// Write the [`TypeResolution`] `resolution` as a WGSL type.
fn write_type_resolution(&self, resolution: &TypeResolution, out: &mut W) -> core::fmt::Result {
fn write_type_resolution<W: Write>(
&self,
resolution: &TypeResolution,
out: &mut W,
) -> core::fmt::Result {
match *resolution {
TypeResolution::Handle(handle) => self.write_type(handle, out),
TypeResolution::Value(ref inner) => self.write_type_inner(inner, out),
@@ -106,7 +113,7 @@ pub trait TypeContext<W: Write> {
fn try_write_type_inner<C, W>(ctx: &C, inner: &TypeInner, out: &mut W) -> Result<(), WriteTypeError>
where
C: TypeContext<W> + ?Sized,
C: TypeContext + ?Sized,
W: Write,
{
match *inner {