Skip to main content

naga/back/wgsl/
writer.rs

1use alloc::{
2    format,
3    string::{String, ToString},
4    vec,
5    vec::Vec,
6};
7use core::fmt::Write;
8
9use super::Error;
10use super::ToWgslIfImplemented as _;
11use crate::{back::wgsl::polyfill::InversePolyfill, common::wgsl::TypeContext};
12use crate::{
13    back::{self, Baked},
14    common::{
15        self,
16        wgsl::{address_space_str, ToWgsl, TryToWgsl},
17    },
18    proc::{self, NameKey},
19    valid, Handle, Module, ShaderStage, TypeInner,
20};
21
22/// Shorthand result used internally by the backend
23type BackendResult = Result<(), Error>;
24
25/// WGSL [attribute](https://gpuweb.github.io/gpuweb/wgsl/#attributes)
26enum Attribute {
27    Binding(u32),
28    BuiltIn(crate::BuiltIn),
29    Group(u32),
30    Invariant,
31    Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>),
32    Location(u32),
33    BlendSrc(u32),
34    Stage(ShaderStage),
35    WorkGroupSize([u32; 3]),
36    MeshStage(String),
37    TaskPayload(String),
38    PerPrimitive,
39    IncomingRayPayload(String),
40}
41
42/// The WGSL form that `write_expr_with_indirection` should use to render a Naga
43/// expression.
44///
45/// Sometimes a Naga `Expression` alone doesn't provide enough information to
46/// choose the right rendering for it in WGSL. For example, one natural WGSL
47/// rendering of a Naga `LocalVariable(x)` expression might be `&x`, since
48/// `LocalVariable` produces a pointer to the local variable's storage. But when
49/// rendering a `Store` statement, the `pointer` operand must be the left hand
50/// side of a WGSL assignment, so the proper rendering is `x`.
51///
52/// The caller of `write_expr_with_indirection` must provide an `Expected` value
53/// to indicate how ambiguous expressions should be rendered.
54#[derive(Clone, Copy, Debug)]
55enum Indirection {
56    /// Render pointer-construction expressions as WGSL `ptr`-typed expressions.
57    ///
58    /// This is the right choice for most cases. Whenever a Naga pointer
59    /// expression is not the `pointer` operand of a `Load` or `Store`, it
60    /// must be a WGSL pointer expression.
61    Ordinary,
62
63    /// Render pointer-construction expressions as WGSL reference-typed
64    /// expressions.
65    ///
66    /// For example, this is the right choice for the `pointer` operand when
67    /// rendering a `Store` statement as a WGSL assignment.
68    Reference,
69}
70
71bitflags::bitflags! {
72    #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
73    #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
74    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
75    pub struct WriterFlags: u32 {
76        /// Always annotate the type information instead of inferring.
77        const EXPLICIT_TYPES = 0x1;
78    }
79}
80
81#[expect(missing_debug_implementations, reason = "would be way too verbose?")]
82pub struct Writer<W> {
83    out: W,
84    flags: WriterFlags,
85    names: crate::FastHashMap<NameKey, String>,
86    namer: proc::Namer,
87    named_expressions: crate::NamedExpressions,
88    required_polyfills: crate::FastIndexSet<InversePolyfill>,
89}
90
91impl<W: Write> Writer<W> {
92    pub fn new(out: W, flags: WriterFlags) -> Self {
93        Writer {
94            out,
95            flags,
96            names: crate::FastHashMap::default(),
97            namer: proc::Namer::default(),
98            named_expressions: crate::NamedExpressions::default(),
99            required_polyfills: crate::FastIndexSet::default(),
100        }
101    }
102
103    fn reset(&mut self, module: &Module) {
104        self.names.clear();
105        self.namer.reset(
106            module,
107            &crate::keywords::wgsl::RESERVED_SET,
108            &crate::keywords::wgsl::BUILTIN_IDENTIFIER_SET,
109            // an identifier must not start with two underscore
110            proc::CaseInsensitiveKeywordSet::empty(),
111            &["__", "_naga"],
112            &mut self.names,
113        );
114        self.named_expressions.clear();
115        self.required_polyfills.clear();
116    }
117
118    /// Determine if `ty` is the Naga IR presentation of a WGSL builtin type.
119    ///
120    /// Return true if `ty` refers to the Naga IR form of a WGSL builtin type
121    /// like `__atomic_compare_exchange_result`.
122    ///
123    /// Even though the module may use the type, the WGSL backend should avoid
124    /// emitting a definition for it, since it is [predeclared] in WGSL.
125    ///
126    /// This also covers types like [`NagaExternalTextureParams`], which other
127    /// backends use to lower WGSL constructs like external textures to their
128    /// implementations. WGSL can express these directly, so the types need not
129    /// be emitted.
130    ///
131    /// [predeclared]: https://www.w3.org/TR/WGSL/#predeclared
132    /// [`NagaExternalTextureParams`]: crate::ir::SpecialTypes::external_texture_params
133    fn is_builtin_wgsl_struct(&self, module: &Module, ty: Handle<crate::Type>) -> bool {
134        module
135            .special_types
136            .predeclared_types
137            .values()
138            .any(|t| *t == ty)
139            || Some(ty) == module.special_types.external_texture_params
140            || Some(ty) == module.special_types.external_texture_transfer_function
141    }
142
143    pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult {
144        self.reset(module);
145
146        // Write all `enable` declarations
147        self.write_enable_declarations(module)?;
148
149        // Write all structs
150        for (handle, ty) in module.types.iter() {
151            if let TypeInner::Struct { ref members, .. } = ty.inner {
152                {
153                    if !self.is_builtin_wgsl_struct(module, handle) {
154                        self.write_struct(module, handle, members)?;
155                        writeln!(self.out)?;
156                    }
157                }
158            }
159        }
160
161        // Write all named constants
162        let mut constants = module
163            .constants
164            .iter()
165            .filter(|&(_, c)| c.name.is_some())
166            .peekable();
167        while let Some((handle, _)) = constants.next() {
168            self.write_global_constant(module, handle)?;
169            // Add extra newline for readability on last iteration
170            if constants.peek().is_none() {
171                writeln!(self.out)?;
172            }
173        }
174
175        // Write all overrides
176        let mut overrides = module.overrides.iter().peekable();
177        while let Some((handle, _)) = overrides.next() {
178            self.write_override(module, handle)?;
179            // Add extra newline for readability on last iteration
180            if overrides.peek().is_none() {
181                writeln!(self.out)?;
182            }
183        }
184
185        // Write all globals
186        for (ty, global) in module.global_variables.iter() {
187            self.write_global(module, global, ty)?;
188        }
189
190        if !module.global_variables.is_empty() {
191            // Add extra newline for readability
192            writeln!(self.out)?;
193        }
194
195        // Write all regular functions
196        for (handle, function) in module.functions.iter() {
197            let fun_info = &info[handle];
198
199            let func_ctx = back::FunctionCtx {
200                ty: back::FunctionType::Function(handle),
201                info: fun_info,
202                expressions: &function.expressions,
203                named_expressions: &function.named_expressions,
204            };
205
206            // Write the function
207            self.write_function(module, function, &func_ctx)?;
208
209            writeln!(self.out)?;
210        }
211
212        // Write all entry points
213        for (index, ep) in module.entry_points.iter().enumerate() {
214            let attributes = match ep.stage {
215                ShaderStage::Vertex | ShaderStage::Fragment => vec![Attribute::Stage(ep.stage)],
216                ShaderStage::Compute => vec![
217                    Attribute::Stage(ShaderStage::Compute),
218                    Attribute::WorkGroupSize(ep.workgroup_size),
219                ],
220                ShaderStage::Mesh => {
221                    let mesh_output_name = module.global_variables
222                        [ep.mesh_info.as_ref().unwrap().output_variable]
223                        .name
224                        .clone()
225                        .unwrap();
226                    let mut mesh_attrs = vec![
227                        Attribute::MeshStage(mesh_output_name),
228                        Attribute::WorkGroupSize(ep.workgroup_size),
229                    ];
230                    if let Some(task_payload) = ep.task_payload {
231                        let payload_name =
232                            module.global_variables[task_payload].name.clone().unwrap();
233                        mesh_attrs.push(Attribute::TaskPayload(payload_name));
234                    }
235                    mesh_attrs
236                }
237                ShaderStage::Task => {
238                    let payload_name = module.global_variables[ep.task_payload.unwrap()]
239                        .name
240                        .clone()
241                        .unwrap();
242                    vec![
243                        Attribute::Stage(ShaderStage::Task),
244                        Attribute::TaskPayload(payload_name),
245                        Attribute::WorkGroupSize(ep.workgroup_size),
246                    ]
247                }
248                ShaderStage::RayGeneration => vec![Attribute::Stage(ShaderStage::RayGeneration)],
249                ShaderStage::AnyHit | ShaderStage::ClosestHit | ShaderStage::Miss => {
250                    let payload_name = module.global_variables[ep.incoming_ray_payload.unwrap()]
251                        .name
252                        .clone()
253                        .unwrap();
254                    vec![
255                        Attribute::Stage(ep.stage),
256                        Attribute::IncomingRayPayload(payload_name),
257                    ]
258                }
259            };
260            self.write_attributes_line(&attributes)?;
261
262            let func_ctx = back::FunctionCtx {
263                ty: back::FunctionType::EntryPoint(index as u16),
264                info: info.get_entry_point(index),
265                expressions: &ep.function.expressions,
266                named_expressions: &ep.function.named_expressions,
267            };
268            self.write_function(module, &ep.function, &func_ctx)?;
269
270            if index < module.entry_points.len() - 1 {
271                writeln!(self.out)?;
272            }
273        }
274
275        // Write any polyfills that were required.
276        for polyfill in &self.required_polyfills {
277            writeln!(self.out)?;
278            write!(self.out, "{}", polyfill.source)?;
279            writeln!(self.out)?;
280        }
281
282        Ok(())
283    }
284
285    /// Helper method which writes all the `enable` declarations
286    /// needed for a module.
287    fn write_enable_declarations(&mut self, module: &Module) -> BackendResult {
288        #[derive(Default)]
289        struct RequiredEnabled {
290            f16: bool,
291            int16: bool,
292            dual_source_blending: bool,
293            clip_distances: bool,
294            mesh_shaders: bool,
295            primitive_index: bool,
296            cooperative_matrix: bool,
297            draw_index: bool,
298            ray_tracing_pipeline: bool,
299            per_vertex: bool,
300            binding_array: bool,
301            debug_printf: bool,
302        }
303        let mut needed = RequiredEnabled {
304            mesh_shaders: module.uses_mesh_shaders(),
305            ..Default::default()
306        };
307
308        let check_binding = |binding: &crate::Binding, needed: &mut RequiredEnabled| match *binding
309        {
310            crate::Binding::Location {
311                blend_src: Some(_), ..
312            } => {
313                needed.dual_source_blending = true;
314            }
315            crate::Binding::BuiltIn(crate::BuiltIn::ClipDistances) => {
316                needed.clip_distances = true;
317            }
318            crate::Binding::BuiltIn(crate::BuiltIn::PrimitiveIndex) => {
319                needed.primitive_index = true;
320            }
321            crate::Binding::Location {
322                per_primitive: true,
323                ..
324            } => {
325                needed.mesh_shaders = true;
326            }
327            crate::Binding::Location {
328                interpolation: Some(crate::Interpolation::PerVertex),
329                ..
330            } => {
331                needed.per_vertex = true;
332            }
333            crate::Binding::BuiltIn(crate::BuiltIn::DrawIndex) => needed.draw_index = true,
334            crate::Binding::BuiltIn(
335                crate::BuiltIn::RayInvocationId
336                | crate::BuiltIn::NumRayInvocations
337                | crate::BuiltIn::InstanceCustomData
338                | crate::BuiltIn::GeometryIndex
339                | crate::BuiltIn::WorldRayOrigin
340                | crate::BuiltIn::WorldRayDirection
341                | crate::BuiltIn::ObjectRayOrigin
342                | crate::BuiltIn::ObjectRayDirection
343                | crate::BuiltIn::RayTmin
344                | crate::BuiltIn::RayTCurrentMax
345                | crate::BuiltIn::ObjectToWorld
346                | crate::BuiltIn::WorldToObject
347                | crate::BuiltIn::HitKind
348                | crate::BuiltIn::HitBarycentrics,
349            ) => {
350                needed.ray_tracing_pipeline = true;
351            }
352            _ => {}
353        };
354
355        // Determine which `enable` declarations are needed
356        for (_, ty) in module.types.iter() {
357            match ty.inner {
358                TypeInner::Scalar(scalar)
359                | TypeInner::Vector { scalar, .. }
360                | TypeInner::Matrix { scalar, .. } => {
361                    needed.f16 |= scalar == crate::Scalar::F16;
362                    needed.int16 |= scalar == crate::Scalar::I16 || scalar == crate::Scalar::U16;
363                }
364                TypeInner::Struct { ref members, .. } => {
365                    for binding in members.iter().filter_map(|m| m.binding.as_ref()) {
366                        check_binding(binding, &mut needed);
367                    }
368                }
369                TypeInner::CooperativeMatrix { .. } => {
370                    needed.cooperative_matrix = true;
371                }
372                TypeInner::AccelerationStructure { .. } => {
373                    needed.ray_tracing_pipeline = true;
374                }
375                TypeInner::BindingArray { .. } => {
376                    needed.binding_array = true;
377                }
378                _ => {}
379            }
380        }
381
382        for ep in &module.entry_points {
383            if let Some(res) = ep.function.result.as_ref().and_then(|a| a.binding.as_ref()) {
384                check_binding(res, &mut needed);
385            }
386            for arg in ep
387                .function
388                .arguments
389                .iter()
390                .filter_map(|a| a.binding.as_ref())
391            {
392                check_binding(arg, &mut needed);
393            }
394        }
395
396        if module.global_variables.iter().any(|gv| {
397            gv.1.space == crate::AddressSpace::IncomingRayPayload
398                || gv.1.space == crate::AddressSpace::RayPayload
399        }) {
400            needed.ray_tracing_pipeline = true;
401        }
402
403        if module.entry_points.iter().any(|ep| {
404            matches!(
405                ep.stage,
406                ShaderStage::RayGeneration
407                    | ShaderStage::AnyHit
408                    | ShaderStage::ClosestHit
409                    | ShaderStage::Miss
410            )
411        }) {
412            needed.ray_tracing_pipeline = true;
413        }
414
415        if module.global_variables.iter().any(|gv| {
416            gv.1.space == crate::AddressSpace::IncomingRayPayload
417                || gv.1.space == crate::AddressSpace::RayPayload
418        }) {
419            needed.ray_tracing_pipeline = true;
420        }
421
422        if module.entry_points.iter().any(|ep| {
423            matches!(
424                ep.stage,
425                ShaderStage::RayGeneration
426                    | ShaderStage::AnyHit
427                    | ShaderStage::ClosestHit
428                    | ShaderStage::Miss
429            )
430        }) {
431            needed.ray_tracing_pipeline = true;
432        }
433
434        needed.debug_printf = module.uses_debug_printf();
435
436        // Write required declarations
437        let mut any_written = false;
438        if needed.f16 {
439            writeln!(self.out, "enable f16;")?;
440            any_written = true;
441        }
442        if needed.int16 {
443            writeln!(self.out, "enable wgpu_int16;")?;
444            any_written = true;
445        }
446        if needed.dual_source_blending {
447            writeln!(self.out, "enable dual_source_blending;")?;
448            any_written = true;
449        }
450        if needed.clip_distances {
451            writeln!(self.out, "enable clip_distances;")?;
452            any_written = true;
453        }
454        if module.uses_mesh_shaders() {
455            writeln!(self.out, "enable wgpu_mesh_shader;")?;
456            any_written = true;
457        }
458        if needed.binding_array {
459            writeln!(self.out, "enable wgpu_binding_array;")?;
460            any_written = true;
461        }
462        if needed.draw_index {
463            writeln!(self.out, "enable draw_index;")?;
464            any_written = true;
465        }
466        if needed.primitive_index {
467            writeln!(self.out, "enable primitive_index;")?;
468            any_written = true;
469        }
470        if needed.cooperative_matrix {
471            writeln!(self.out, "enable wgpu_cooperative_matrix;")?;
472            any_written = true;
473        }
474        if needed.ray_tracing_pipeline {
475            writeln!(self.out, "enable wgpu_ray_tracing_pipeline;")?;
476            any_written = true;
477        }
478        if needed.per_vertex {
479            writeln!(self.out, "enable wgpu_per_vertex;")?;
480            any_written = true;
481        }
482        if needed.debug_printf {
483            writeln!(self.out, "enable wgpu_debug_printf;")?;
484            any_written = true;
485        }
486        if any_written {
487            // Empty line for readability
488            writeln!(self.out)?;
489        }
490
491        Ok(())
492    }
493
494    /// Helper method used to write
495    /// [functions](https://gpuweb.github.io/gpuweb/wgsl/#functions)
496    ///
497    /// # Notes
498    /// Ends in a newline
499    fn write_function(
500        &mut self,
501        module: &Module,
502        func: &crate::Function,
503        func_ctx: &back::FunctionCtx<'_>,
504    ) -> BackendResult {
505        let func_name = match func_ctx.ty {
506            back::FunctionType::EntryPoint(index) => &self.names[&NameKey::EntryPoint(index)],
507            back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],
508        };
509
510        // Write function name
511        write!(self.out, "fn {func_name}(")?;
512
513        // Write function arguments
514        for (index, arg) in func.arguments.iter().enumerate() {
515            // Write argument attribute if a binding is present
516            if let Some(ref binding) = arg.binding {
517                self.write_attributes(&map_binding_to_attribute(binding))?;
518            }
519            // Write argument name
520            let argument_name = &self.names[&func_ctx.argument_key(index as u32)];
521
522            write!(self.out, "{argument_name}: ")?;
523            // Write argument type
524            self.write_type(module, arg.ty)?;
525            if index < func.arguments.len() - 1 {
526                // Add a separator between args
527                write!(self.out, ", ")?;
528            }
529        }
530
531        write!(self.out, ")")?;
532
533        // Write function return type
534        if let Some(ref result) = func.result {
535            write!(self.out, " -> ")?;
536            if let Some(ref binding) = result.binding {
537                self.write_attributes(&map_binding_to_attribute(binding))?;
538            }
539            self.write_type(module, result.ty)?;
540        }
541
542        write!(self.out, " {{")?;
543        writeln!(self.out)?;
544
545        // Write function local variables
546        for (handle, local) in func.local_variables.iter() {
547            // Write indentation (only for readability)
548            write!(self.out, "{}", back::INDENT)?;
549
550            // Write the local name
551            // The leading space is important
552            write!(self.out, "var {}: ", self.names[&func_ctx.name_key(handle)])?;
553
554            // Write the local type
555            self.write_type(module, local.ty)?;
556
557            // Write the local initializer if needed
558            if let Some(init) = local.init {
559                // Put the equal signal only if there's a initializer
560                // The leading and trailing spaces aren't needed but help with readability
561                write!(self.out, " = ")?;
562
563                // Write the constant
564                // `write_constant` adds no trailing or leading space/newline
565                self.write_expr(module, init, func_ctx)?;
566            }
567
568            // Finish the local with `;` and add a newline (only for readability)
569            writeln!(self.out, ";")?
570        }
571
572        if !func.local_variables.is_empty() {
573            writeln!(self.out)?;
574        }
575
576        // Write the function body (statement list)
577        for sta in func.body.iter() {
578            // The indentation should always be 1 when writing the function body
579            self.write_stmt(module, sta, func_ctx, back::Level(1))?;
580        }
581
582        writeln!(self.out, "}}")?;
583
584        self.named_expressions.clear();
585
586        Ok(())
587    }
588
589    /// Helper method to write an attribute.
590    ///
591    /// Write `attributes` to `out`, with each one followed by a trailing space.
592    fn write_attributes_to(out: &mut impl Write, attributes: &[Attribute]) -> BackendResult {
593        for attribute in attributes {
594            match *attribute {
595                Attribute::Location(id) => write!(out, "@location({id}) ")?,
596                Attribute::BlendSrc(blend_src) => write!(out, "@blend_src({blend_src}) ")?,
597                Attribute::BuiltIn(builtin_attrib) => {
598                    let builtin = builtin_attrib.to_wgsl_if_implemented()?;
599                    write!(out, "@builtin({builtin}) ")?;
600                }
601                Attribute::Stage(shader_stage) => {
602                    let stage_str = match shader_stage {
603                        ShaderStage::Vertex => "vertex",
604                        ShaderStage::Fragment => "fragment",
605                        ShaderStage::Compute => "compute",
606                        ShaderStage::Task => "task",
607                        //Handled by another variant in the Attribute enum, so this code should never be hit.
608                        ShaderStage::Mesh => unreachable!(),
609                        ShaderStage::RayGeneration => "ray_generation",
610                        ShaderStage::AnyHit => "any_hit",
611                        ShaderStage::ClosestHit => "closest_hit",
612                        ShaderStage::Miss => "miss",
613                    };
614
615                    write!(out, "@{stage_str} ")?;
616                }
617                Attribute::WorkGroupSize(size) => {
618                    write!(
619                        out,
620                        "@workgroup_size({}, {}, {}) ",
621                        size[0], size[1], size[2]
622                    )?;
623                }
624                Attribute::Binding(id) => write!(out, "@binding({id}) ")?,
625                Attribute::Group(id) => write!(out, "@group({id}) ")?,
626                Attribute::Invariant => write!(out, "@invariant ")?,
627                Attribute::Interpolate(interpolation, sampling) => {
628                    if sampling.is_some() && sampling != Some(crate::Sampling::Center) {
629                        let interpolation = interpolation
630                            .unwrap_or(crate::Interpolation::Perspective)
631                            .to_wgsl();
632                        let sampling = sampling.unwrap_or(crate::Sampling::Center).to_wgsl();
633                        write!(out, "@interpolate({interpolation}, {sampling}) ")?;
634                    } else if interpolation.is_some()
635                        && interpolation != Some(crate::Interpolation::Perspective)
636                    {
637                        let interpolation = interpolation
638                            .unwrap_or(crate::Interpolation::Perspective)
639                            .to_wgsl();
640                        write!(out, "@interpolate({interpolation}) ")?;
641                    }
642                }
643                Attribute::MeshStage(ref name) => {
644                    write!(out, "@mesh({name}) ")?;
645                }
646                Attribute::TaskPayload(ref payload_name) => {
647                    write!(out, "@payload({payload_name}) ")?;
648                }
649                Attribute::PerPrimitive => write!(out, "@per_primitive ")?,
650                Attribute::IncomingRayPayload(ref payload_name) => {
651                    write!(out, "@incoming_payload({payload_name}) ")?;
652                }
653            };
654        }
655        Ok(())
656    }
657
658    /// Write `attributes` followed by a single trailing space, for use where more
659    /// content follows on the same line (e.g. before a parameter name).
660    fn write_attributes(&mut self, attributes: &[Attribute]) -> BackendResult {
661        Self::write_attributes_to(&mut self.out, attributes)
662    }
663
664    /// Write `attributes` on their own line, followed by a newline. Attributes are
665    /// rendered into a buffer first so the trailing space `write_attributes_to`
666    /// puts after the last one can be trimmed before it reaches the line.
667    fn write_attributes_line(&mut self, attributes: &[Attribute]) -> BackendResult {
668        let mut buf = String::new();
669        Self::write_attributes_to(&mut buf, attributes)?;
670        writeln!(self.out, "{}", buf.trim_end())?;
671        Ok(())
672    }
673
674    /// Helper method used to write structs
675    /// Write the full declaration of a struct type.
676    ///
677    /// Write out a definition of the struct type referred to by
678    /// `handle` in `module`. The output will be an instance of the
679    /// `struct_decl` production in the WGSL grammar.
680    ///
681    /// Use `members` as the list of `handle`'s members. (This
682    /// function is usually called after matching a `TypeInner`, so
683    /// the callers already have the members at hand.)
684    fn write_struct(
685        &mut self,
686        module: &Module,
687        handle: Handle<crate::Type>,
688        members: &[crate::StructMember],
689    ) -> BackendResult {
690        write!(self.out, "struct {}", self.names[&NameKey::Type(handle)])?;
691        write!(self.out, " {{")?;
692        writeln!(self.out)?;
693        for (index, member) in members.iter().enumerate() {
694            // The indentation is only for readability
695            write!(self.out, "{}", back::INDENT)?;
696            if let Some(ref binding) = member.binding {
697                self.write_attributes(&map_binding_to_attribute(binding))?;
698            }
699            // Write struct member name and type
700            let member_name = &self.names[&NameKey::StructMember(handle, index as u32)];
701            write!(self.out, "{member_name}: ")?;
702            self.write_type(module, member.ty)?;
703            write!(self.out, ",")?;
704            writeln!(self.out)?;
705        }
706
707        writeln!(self.out, "}}")?;
708
709        Ok(())
710    }
711
712    fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {
713        // This actually can't be factored out into a nice constructor method,
714        // because the borrow checker needs to be able to see that the borrows
715        // of `self.names` and `self.out` are disjoint.
716        let type_context = WriterTypeContext {
717            module,
718            names: &self.names,
719        };
720        type_context.write_type(ty, &mut self.out)?;
721
722        Ok(())
723    }
724
725    fn write_type_resolution(
726        &mut self,
727        module: &Module,
728        resolution: &proc::TypeResolution,
729    ) -> BackendResult {
730        // This actually can't be factored out into a nice constructor method,
731        // because the borrow checker needs to be able to see that the borrows
732        // of `self.names` and `self.out` are disjoint.
733        let type_context = WriterTypeContext {
734            module,
735            names: &self.names,
736        };
737        type_context.write_type_resolution(resolution, &mut self.out)?;
738
739        Ok(())
740    }
741
742    /// Helper method used to write statements
743    ///
744    /// # Notes
745    /// Always adds a newline
746    fn write_stmt(
747        &mut self,
748        module: &Module,
749        stmt: &crate::Statement,
750        func_ctx: &back::FunctionCtx<'_>,
751        level: back::Level,
752    ) -> BackendResult {
753        use crate::{Expression, Statement};
754
755        match *stmt {
756            Statement::Emit(ref range) => {
757                for handle in range.clone() {
758                    let info = &func_ctx.info[handle];
759                    let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) {
760                        // Front end provides names for all variables at the start of writing.
761                        // But we write them to step by step. We need to recache them
762                        // Otherwise, we could accidentally write variable name instead of full expression.
763                        // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords.
764                        Some(self.namer.call(name))
765                    } else {
766                        let expr = &func_ctx.expressions[handle];
767                        let min_ref_count = expr.bake_ref_count();
768                        // Forcefully creating baking expressions in some cases to help with readability
769                        let required_baking_expr = match *expr {
770                            Expression::ImageLoad { .. }
771                            | Expression::ImageQuery { .. }
772                            | Expression::ImageSample { .. } => true,
773                            _ => false,
774                        };
775                        if min_ref_count <= info.ref_count || required_baking_expr {
776                            Some(Baked(handle).to_string())
777                        } else {
778                            None
779                        }
780                    };
781
782                    if let Some(name) = expr_name {
783                        write!(self.out, "{level}")?;
784                        self.start_named_expr(module, handle, func_ctx, &name)?;
785                        self.write_expr(module, handle, func_ctx)?;
786                        self.named_expressions.insert(handle, name);
787                        writeln!(self.out, ";")?;
788                    }
789                }
790            }
791            // TODO: copy-paste from glsl-out
792            Statement::If {
793                condition,
794                ref accept,
795                ref reject,
796            } => {
797                write!(self.out, "{level}")?;
798                write!(self.out, "if ")?;
799                self.write_expr(module, condition, func_ctx)?;
800                writeln!(self.out, " {{")?;
801
802                let l2 = level.next();
803                for sta in accept {
804                    // Increase indentation to help with readability
805                    self.write_stmt(module, sta, func_ctx, l2)?;
806                }
807
808                // If there are no statements in the reject block we skip writing it
809                // This is only for readability
810                if !reject.is_empty() {
811                    writeln!(self.out, "{level}}} else {{")?;
812
813                    for sta in reject {
814                        // Increase indentation to help with readability
815                        self.write_stmt(module, sta, func_ctx, l2)?;
816                    }
817                }
818
819                writeln!(self.out, "{level}}}")?
820            }
821            Statement::Return { value } => {
822                write!(self.out, "{level}")?;
823                write!(self.out, "return")?;
824                if let Some(return_value) = value {
825                    // The leading space is important
826                    write!(self.out, " ")?;
827                    self.write_expr(module, return_value, func_ctx)?;
828                }
829                writeln!(self.out, ";")?;
830            }
831            // TODO: copy-paste from glsl-out
832            Statement::Kill => {
833                write!(self.out, "{level}")?;
834                writeln!(self.out, "discard;")?
835            }
836            Statement::Store { pointer, value } => {
837                write!(self.out, "{level}")?;
838
839                let is_atomic_pointer = func_ctx
840                    .resolve_type(pointer, &module.types)
841                    .is_atomic_pointer(&module.types);
842
843                if is_atomic_pointer {
844                    write!(self.out, "atomicStore(")?;
845                    self.write_expr(module, pointer, func_ctx)?;
846                    write!(self.out, ", ")?;
847                    self.write_expr(module, value, func_ctx)?;
848                    write!(self.out, ")")?;
849                } else {
850                    self.write_expr_with_indirection(
851                        module,
852                        pointer,
853                        func_ctx,
854                        Indirection::Reference,
855                    )?;
856                    write!(self.out, " = ")?;
857                    self.write_expr(module, value, func_ctx)?;
858                }
859                writeln!(self.out, ";")?
860            }
861            Statement::Call {
862                function,
863                ref arguments,
864                result,
865            } => {
866                write!(self.out, "{level}")?;
867                if let Some(expr) = result {
868                    let name = Baked(expr).to_string();
869                    self.start_named_expr(module, expr, func_ctx, &name)?;
870                    self.named_expressions.insert(expr, name);
871                }
872                let func_name = &self.names[&NameKey::Function(function)];
873                write!(self.out, "{func_name}(")?;
874                for (index, &argument) in arguments.iter().enumerate() {
875                    if index != 0 {
876                        write!(self.out, ", ")?;
877                    }
878                    self.write_expr(module, argument, func_ctx)?;
879                }
880                writeln!(self.out, ");")?
881            }
882            Statement::Atomic {
883                pointer,
884                ref fun,
885                value,
886                result,
887            } => {
888                write!(self.out, "{level}")?;
889                if let Some(result) = result {
890                    let res_name = Baked(result).to_string();
891                    self.start_named_expr(module, result, func_ctx, &res_name)?;
892                    self.named_expressions.insert(result, res_name);
893                }
894
895                let fun_str = fun.to_wgsl();
896                write!(self.out, "atomic{fun_str}(")?;
897                self.write_expr(module, pointer, func_ctx)?;
898                if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {
899                    write!(self.out, ", ")?;
900                    self.write_expr(module, cmp, func_ctx)?;
901                }
902                write!(self.out, ", ")?;
903                self.write_expr(module, value, func_ctx)?;
904                writeln!(self.out, ");")?
905            }
906            Statement::ImageAtomic {
907                image,
908                coordinate,
909                array_index,
910                ref fun,
911                value,
912            } => {
913                write!(self.out, "{level}")?;
914                let fun_str = fun.to_wgsl();
915                write!(self.out, "textureAtomic{fun_str}(")?;
916                self.write_expr(module, image, func_ctx)?;
917                write!(self.out, ", ")?;
918                self.write_expr(module, coordinate, func_ctx)?;
919                if let Some(array_index_expr) = array_index {
920                    write!(self.out, ", ")?;
921                    self.write_expr(module, array_index_expr, func_ctx)?;
922                }
923                write!(self.out, ", ")?;
924                self.write_expr(module, value, func_ctx)?;
925                writeln!(self.out, ");")?;
926            }
927            Statement::WorkGroupUniformLoad { pointer, result } => {
928                write!(self.out, "{level}")?;
929                // TODO: Obey named expressions here.
930                let res_name = Baked(result).to_string();
931                self.start_named_expr(module, result, func_ctx, &res_name)?;
932                self.named_expressions.insert(result, res_name);
933                write!(self.out, "workgroupUniformLoad(")?;
934                self.write_expr(module, pointer, func_ctx)?;
935                writeln!(self.out, ");")?;
936            }
937            Statement::ImageStore {
938                image,
939                coordinate,
940                array_index,
941                value,
942            } => {
943                write!(self.out, "{level}")?;
944                write!(self.out, "textureStore(")?;
945                self.write_expr(module, image, func_ctx)?;
946                write!(self.out, ", ")?;
947                self.write_expr(module, coordinate, func_ctx)?;
948                if let Some(array_index_expr) = array_index {
949                    write!(self.out, ", ")?;
950                    self.write_expr(module, array_index_expr, func_ctx)?;
951                }
952                write!(self.out, ", ")?;
953                self.write_expr(module, value, func_ctx)?;
954                writeln!(self.out, ");")?;
955            }
956            // TODO: copy-paste from glsl-out
957            Statement::Block(ref block) => {
958                write!(self.out, "{level}")?;
959                writeln!(self.out, "{{")?;
960                for sta in block.iter() {
961                    // Increase the indentation to help with readability
962                    self.write_stmt(module, sta, func_ctx, level.next())?
963                }
964                writeln!(self.out, "{level}}}")?
965            }
966            Statement::Switch {
967                selector,
968                ref cases,
969            } => {
970                // Start the switch
971                write!(self.out, "{level}")?;
972                write!(self.out, "switch ")?;
973                self.write_expr(module, selector, func_ctx)?;
974                writeln!(self.out, " {{")?;
975
976                let l2 = level.next();
977                let mut new_case = true;
978                for case in cases {
979                    if case.fall_through && !case.body.is_empty() {
980                        // TODO: we could do the same workaround as we did for the HLSL backend
981                        return Err(Error::Unimplemented(
982                            "fall-through switch case block".into(),
983                        ));
984                    }
985
986                    match case.value {
987                        crate::SwitchValue::I32(value) => {
988                            if new_case {
989                                write!(self.out, "{l2}case ")?;
990                            }
991                            write!(self.out, "{value}")?;
992                        }
993                        crate::SwitchValue::U32(value) => {
994                            if new_case {
995                                write!(self.out, "{l2}case ")?;
996                            }
997                            write!(self.out, "{value}u")?;
998                        }
999                        crate::SwitchValue::Default => {
1000                            if new_case {
1001                                if case.fall_through {
1002                                    write!(self.out, "{l2}case ")?;
1003                                } else {
1004                                    write!(self.out, "{l2}")?;
1005                                }
1006                            }
1007                            write!(self.out, "default")?;
1008                        }
1009                    }
1010
1011                    new_case = !case.fall_through;
1012
1013                    if case.fall_through {
1014                        write!(self.out, ", ")?;
1015                    } else {
1016                        writeln!(self.out, ": {{")?;
1017                    }
1018
1019                    for sta in case.body.iter() {
1020                        self.write_stmt(module, sta, func_ctx, l2.next())?;
1021                    }
1022
1023                    if !case.fall_through {
1024                        writeln!(self.out, "{l2}}}")?;
1025                    }
1026                }
1027
1028                writeln!(self.out, "{level}}}")?
1029            }
1030            Statement::Loop {
1031                ref body,
1032                ref continuing,
1033                break_if,
1034            } => {
1035                write!(self.out, "{level}")?;
1036                writeln!(self.out, "loop {{")?;
1037
1038                let l2 = level.next();
1039                for sta in body.iter() {
1040                    self.write_stmt(module, sta, func_ctx, l2)?;
1041                }
1042
1043                // The continuing is optional so we don't need to write it if
1044                // it is empty, but the `break if` counts as a continuing statement
1045                // so even if `continuing` is empty we must generate it if a
1046                // `break if` exists
1047                if !continuing.is_empty() || break_if.is_some() {
1048                    writeln!(self.out, "{l2}continuing {{")?;
1049                    for sta in continuing.iter() {
1050                        self.write_stmt(module, sta, func_ctx, l2.next())?;
1051                    }
1052
1053                    // The `break if` is always the last
1054                    // statement of the `continuing` block
1055                    if let Some(condition) = break_if {
1056                        // The trailing space is important
1057                        write!(self.out, "{}break if ", l2.next())?;
1058                        self.write_expr(module, condition, func_ctx)?;
1059                        // Close the `break if` statement
1060                        writeln!(self.out, ";")?;
1061                    }
1062
1063                    writeln!(self.out, "{l2}}}")?;
1064                }
1065
1066                writeln!(self.out, "{level}}}")?
1067            }
1068            Statement::Break => {
1069                writeln!(self.out, "{level}break;")?;
1070            }
1071            Statement::Continue => {
1072                writeln!(self.out, "{level}continue;")?;
1073            }
1074            Statement::ControlBarrier(barrier) | Statement::MemoryBarrier(barrier) => {
1075                if barrier.contains(crate::Barrier::STORAGE) {
1076                    writeln!(self.out, "{level}storageBarrier();")?;
1077                }
1078
1079                if barrier.contains(crate::Barrier::WORK_GROUP) {
1080                    writeln!(self.out, "{level}workgroupBarrier();")?;
1081                }
1082
1083                if barrier.contains(crate::Barrier::SUB_GROUP) {
1084                    writeln!(self.out, "{level}subgroupBarrier();")?;
1085                }
1086
1087                if barrier.contains(crate::Barrier::TEXTURE) {
1088                    writeln!(self.out, "{level}textureBarrier();")?;
1089                }
1090            }
1091            Statement::RayQuery { .. } => unreachable!(),
1092            Statement::SubgroupBallot { result, predicate } => {
1093                write!(self.out, "{level}")?;
1094                let res_name = Baked(result).to_string();
1095                self.start_named_expr(module, result, func_ctx, &res_name)?;
1096                self.named_expressions.insert(result, res_name);
1097
1098                write!(self.out, "subgroupBallot(")?;
1099                if let Some(predicate) = predicate {
1100                    self.write_expr(module, predicate, func_ctx)?;
1101                }
1102                writeln!(self.out, ");")?;
1103            }
1104            Statement::SubgroupCollectiveOperation {
1105                op,
1106                collective_op,
1107                argument,
1108                result,
1109            } => {
1110                write!(self.out, "{level}")?;
1111                let res_name = Baked(result).to_string();
1112                self.start_named_expr(module, result, func_ctx, &res_name)?;
1113                self.named_expressions.insert(result, res_name);
1114
1115                match (collective_op, op) {
1116                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
1117                        write!(self.out, "subgroupAll(")?
1118                    }
1119                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
1120                        write!(self.out, "subgroupAny(")?
1121                    }
1122                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
1123                        write!(self.out, "subgroupAdd(")?
1124                    }
1125                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
1126                        write!(self.out, "subgroupMul(")?
1127                    }
1128                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
1129                        write!(self.out, "subgroupMax(")?
1130                    }
1131                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
1132                        write!(self.out, "subgroupMin(")?
1133                    }
1134                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
1135                        write!(self.out, "subgroupAnd(")?
1136                    }
1137                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
1138                        write!(self.out, "subgroupOr(")?
1139                    }
1140                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
1141                        write!(self.out, "subgroupXor(")?
1142                    }
1143                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
1144                        write!(self.out, "subgroupExclusiveAdd(")?
1145                    }
1146                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
1147                        write!(self.out, "subgroupExclusiveMul(")?
1148                    }
1149                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
1150                        write!(self.out, "subgroupInclusiveAdd(")?
1151                    }
1152                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
1153                        write!(self.out, "subgroupInclusiveMul(")?
1154                    }
1155                    _ => unimplemented!(),
1156                }
1157                self.write_expr(module, argument, func_ctx)?;
1158                writeln!(self.out, ");")?;
1159            }
1160            Statement::SubgroupGather {
1161                mode,
1162                argument,
1163                result,
1164            } => {
1165                write!(self.out, "{level}")?;
1166                let res_name = Baked(result).to_string();
1167                self.start_named_expr(module, result, func_ctx, &res_name)?;
1168                self.named_expressions.insert(result, res_name);
1169
1170                match mode {
1171                    crate::GatherMode::BroadcastFirst => {
1172                        write!(self.out, "subgroupBroadcastFirst(")?;
1173                    }
1174                    crate::GatherMode::Broadcast(_) => {
1175                        write!(self.out, "subgroupBroadcast(")?;
1176                    }
1177                    crate::GatherMode::Shuffle(_) => {
1178                        write!(self.out, "subgroupShuffle(")?;
1179                    }
1180                    crate::GatherMode::ShuffleDown(_) => {
1181                        write!(self.out, "subgroupShuffleDown(")?;
1182                    }
1183                    crate::GatherMode::ShuffleUp(_) => {
1184                        write!(self.out, "subgroupShuffleUp(")?;
1185                    }
1186                    crate::GatherMode::ShuffleXor(_) => {
1187                        write!(self.out, "subgroupShuffleXor(")?;
1188                    }
1189                    crate::GatherMode::QuadBroadcast(_) => {
1190                        write!(self.out, "quadBroadcast(")?;
1191                    }
1192                    crate::GatherMode::QuadSwap(direction) => match direction {
1193                        crate::Direction::X => {
1194                            write!(self.out, "quadSwapX(")?;
1195                        }
1196                        crate::Direction::Y => {
1197                            write!(self.out, "quadSwapY(")?;
1198                        }
1199                        crate::Direction::Diagonal => {
1200                            write!(self.out, "quadSwapDiagonal(")?;
1201                        }
1202                    },
1203                }
1204                self.write_expr(module, argument, func_ctx)?;
1205                match mode {
1206                    crate::GatherMode::BroadcastFirst => {}
1207                    crate::GatherMode::Broadcast(index)
1208                    | crate::GatherMode::Shuffle(index)
1209                    | crate::GatherMode::ShuffleDown(index)
1210                    | crate::GatherMode::ShuffleUp(index)
1211                    | crate::GatherMode::ShuffleXor(index)
1212                    | crate::GatherMode::QuadBroadcast(index) => {
1213                        write!(self.out, ", ")?;
1214                        self.write_expr(module, index, func_ctx)?;
1215                    }
1216                    crate::GatherMode::QuadSwap(_) => {}
1217                }
1218                writeln!(self.out, ");")?;
1219            }
1220            Statement::CooperativeStore { target, ref data } => {
1221                let suffix = if data.row_major { "T" } else { "" };
1222                write!(self.out, "{level}coopStore{suffix}(")?;
1223                self.write_expr(module, target, func_ctx)?;
1224                write!(self.out, ", ")?;
1225                self.write_expr(module, data.pointer, func_ctx)?;
1226                write!(self.out, ", ")?;
1227                self.write_expr(module, data.stride, func_ctx)?;
1228                writeln!(self.out, ");")?
1229            }
1230            Statement::RayPipelineFunction(fun) => match fun {
1231                crate::RayPipelineFunction::TraceRay {
1232                    acceleration_structure,
1233                    descriptor,
1234                    payload,
1235                } => {
1236                    write!(self.out, "{level}traceRay(")?;
1237                    self.write_expr(module, acceleration_structure, func_ctx)?;
1238                    write!(self.out, ", ")?;
1239                    self.write_expr(module, descriptor, func_ctx)?;
1240                    write!(self.out, ", ")?;
1241                    self.write_expr(module, payload, func_ctx)?;
1242                    writeln!(self.out, ");")?
1243                }
1244            },
1245            Statement::DebugPrintf {
1246                ref format,
1247                ref arguments,
1248            } => {
1249                write!(self.out, "{level}debugPrintf(\"{}\"", format)?;
1250                for &arg in arguments {
1251                    write!(self.out, ", ")?;
1252                    self.write_expr(module, arg, func_ctx)?;
1253                }
1254                writeln!(self.out, ");")?;
1255            }
1256        }
1257
1258        Ok(())
1259    }
1260
1261    /// Return the sort of indirection that `expr`'s plain form evaluates to.
1262    ///
1263    /// An expression's 'plain form' is the most general rendition of that
1264    /// expression into WGSL, lacking `&` or `*` operators:
1265    ///
1266    /// - The plain form of `LocalVariable(x)` is simply `x`, which is a reference
1267    ///   to the local variable's storage.
1268    ///
1269    /// - The plain form of `GlobalVariable(g)` is simply `g`, which is usually a
1270    ///   reference to the global variable's storage. However, globals in the
1271    ///   `Handle` address space are immutable, and `GlobalVariable` expressions for
1272    ///   those produce the value directly, not a pointer to it. Such
1273    ///   `GlobalVariable` expressions are `Ordinary`.
1274    ///
1275    /// - `Access` and `AccessIndex` are `Reference` when their `base` operand is a
1276    ///   pointer. If they are applied directly to a composite value, they are
1277    ///   `Ordinary`.
1278    ///
1279    /// Note that `FunctionArgument` expressions are never `Reference`, even when
1280    /// the argument's type is `Pointer`. `FunctionArgument` always evaluates to the
1281    /// argument's value directly, so any pointer it produces is merely the value
1282    /// passed by the caller.
1283    fn plain_form_indirection(
1284        &self,
1285        expr: Handle<crate::Expression>,
1286        module: &Module,
1287        func_ctx: &back::FunctionCtx<'_>,
1288    ) -> Indirection {
1289        use crate::Expression as Ex;
1290
1291        // Named expressions are `let` expressions, which apply the Load Rule,
1292        // so if their type is a Naga pointer, then that must be a WGSL pointer
1293        // as well.
1294        if self.named_expressions.contains_key(&expr) {
1295            return Indirection::Ordinary;
1296        }
1297
1298        match func_ctx.expressions[expr] {
1299            Ex::LocalVariable(_) => Indirection::Reference,
1300            Ex::GlobalVariable(handle) => {
1301                let global = &module.global_variables[handle];
1302                match global.space {
1303                    crate::AddressSpace::Handle => Indirection::Ordinary,
1304                    _ => Indirection::Reference,
1305                }
1306            }
1307            Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {
1308                let base_ty = func_ctx.resolve_type(base, &module.types);
1309                match *base_ty {
1310                    TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } => {
1311                        Indirection::Reference
1312                    }
1313                    _ => Indirection::Ordinary,
1314                }
1315            }
1316            _ => Indirection::Ordinary,
1317        }
1318    }
1319
1320    fn start_named_expr(
1321        &mut self,
1322        module: &Module,
1323        handle: Handle<crate::Expression>,
1324        func_ctx: &back::FunctionCtx,
1325        name: &str,
1326    ) -> BackendResult {
1327        // Write variable name
1328        write!(self.out, "let {name}")?;
1329        if self.flags.contains(WriterFlags::EXPLICIT_TYPES) {
1330            write!(self.out, ": ")?;
1331            // Write variable type
1332            self.write_type_resolution(module, &func_ctx.info[handle].ty)?;
1333        }
1334
1335        write!(self.out, " = ")?;
1336        Ok(())
1337    }
1338
1339    /// Write the ordinary WGSL form of `expr`.
1340    ///
1341    /// See `write_expr_with_indirection` for details.
1342    fn write_expr(
1343        &mut self,
1344        module: &Module,
1345        expr: Handle<crate::Expression>,
1346        func_ctx: &back::FunctionCtx<'_>,
1347    ) -> BackendResult {
1348        self.write_expr_with_indirection(module, expr, func_ctx, Indirection::Ordinary)
1349    }
1350
1351    /// Write `expr` as a WGSL expression with the requested indirection.
1352    ///
1353    /// In terms of the WGSL grammar, the resulting expression is a
1354    /// `singular_expression`. It may be parenthesized. This makes it suitable
1355    /// for use as the operand of a unary or binary operator without worrying
1356    /// about precedence.
1357    ///
1358    /// This does not produce newlines or indentation.
1359    ///
1360    /// The `requested` argument indicates (roughly) whether Naga
1361    /// `Pointer`-valued expressions represent WGSL references or pointers. See
1362    /// `Indirection` for details.
1363    fn write_expr_with_indirection(
1364        &mut self,
1365        module: &Module,
1366        expr: Handle<crate::Expression>,
1367        func_ctx: &back::FunctionCtx<'_>,
1368        requested: Indirection,
1369    ) -> BackendResult {
1370        // If the plain form of the expression is not what we need, emit the
1371        // operator necessary to correct that.
1372        let plain = self.plain_form_indirection(expr, module, func_ctx);
1373        log::trace!(
1374            "expression {:?}={:?} is {:?}, expected {:?}",
1375            expr,
1376            func_ctx.expressions[expr],
1377            plain,
1378            requested,
1379        );
1380        match (requested, plain) {
1381            (Indirection::Ordinary, Indirection::Reference) => {
1382                write!(self.out, "(&")?;
1383                self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1384                write!(self.out, ")")?;
1385            }
1386            (Indirection::Reference, Indirection::Ordinary) => {
1387                write!(self.out, "(*")?;
1388                self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1389                write!(self.out, ")")?;
1390            }
1391            (_, _) => self.write_expr_plain_form(module, expr, func_ctx, plain)?,
1392        }
1393
1394        Ok(())
1395    }
1396
1397    fn write_const_expression(
1398        &mut self,
1399        module: &Module,
1400        expr: Handle<crate::Expression>,
1401        arena: &crate::Arena<crate::Expression>,
1402    ) -> BackendResult {
1403        self.write_possibly_const_expression(module, expr, arena, |writer, expr| {
1404            writer.write_const_expression(module, expr, arena)
1405        })
1406    }
1407
1408    fn write_possibly_const_expression<E>(
1409        &mut self,
1410        module: &Module,
1411        expr: Handle<crate::Expression>,
1412        expressions: &crate::Arena<crate::Expression>,
1413        write_expression: E,
1414    ) -> BackendResult
1415    where
1416        E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
1417    {
1418        use crate::Expression;
1419
1420        match expressions[expr] {
1421            Expression::Literal(literal) => match literal {
1422                crate::Literal::F16(value) => write!(self.out, "{value}h")?,
1423                crate::Literal::F32(value) => write!(self.out, "{value}f")?,
1424                crate::Literal::U16(value) => write!(self.out, "u16({value})")?,
1425                crate::Literal::I16(value) => write!(self.out, "i16({value})")?,
1426                crate::Literal::U32(value) => write!(self.out, "{value}u")?,
1427                crate::Literal::I32(value) => {
1428                    // `-2147483648i` is not valid WGSL. The most negative `i32`
1429                    // value can only be expressed in WGSL using AbstractInt and
1430                    // a unary negation operator.
1431                    if value == i32::MIN {
1432                        write!(self.out, "i32({value})")?;
1433                    } else {
1434                        write!(self.out, "{value}i")?;
1435                    }
1436                }
1437                crate::Literal::Bool(value) => write!(self.out, "{value}")?,
1438                crate::Literal::F64(value) => write!(self.out, "{value:?}lf")?,
1439                crate::Literal::I64(value) => {
1440                    // `-9223372036854775808li` is not valid WGSL. Nor can we simply use the
1441                    // AbstractInt trick above, as AbstractInt also cannot represent
1442                    // `9223372036854775808`. Instead construct the second most negative
1443                    // AbstractInt, subtract one from it, then cast to i64.
1444                    if value == i64::MIN {
1445                        write!(self.out, "i64({} - 1)", value + 1)?;
1446                    } else {
1447                        write!(self.out, "{value}li")?;
1448                    }
1449                }
1450                crate::Literal::U64(value) => write!(self.out, "{value:?}lu")?,
1451                crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1452                    return Err(Error::Custom(
1453                        "Abstract types should not appear in IR presented to backends".into(),
1454                    ));
1455                }
1456            },
1457            Expression::Constant(handle) => {
1458                let constant = &module.constants[handle];
1459                if constant.name.is_some() {
1460                    write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
1461                } else {
1462                    self.write_const_expression(module, constant.init, &module.global_expressions)?;
1463                }
1464            }
1465            Expression::ZeroValue(ty) => {
1466                self.write_type(module, ty)?;
1467                write!(self.out, "()")?;
1468            }
1469            Expression::Compose { ty, ref components } => {
1470                self.write_type(module, ty)?;
1471                write!(self.out, "(")?;
1472                for (index, component) in components.iter().enumerate() {
1473                    if index != 0 {
1474                        write!(self.out, ", ")?;
1475                    }
1476                    write_expression(self, *component)?;
1477                }
1478                write!(self.out, ")")?
1479            }
1480            Expression::Splat { size, value } => {
1481                let size = common::vector_size_str(size);
1482                write!(self.out, "vec{size}(")?;
1483                write_expression(self, value)?;
1484                write!(self.out, ")")?;
1485            }
1486            Expression::Override(handle) => {
1487                write!(self.out, "{}", self.names[&NameKey::Override(handle)])?;
1488            }
1489            _ => unreachable!(),
1490        }
1491
1492        Ok(())
1493    }
1494
1495    /// Write the 'plain form' of `expr`.
1496    ///
1497    /// An expression's 'plain form' is the most general rendition of that
1498    /// expression into WGSL, lacking `&` or `*` operators. The plain forms of
1499    /// `LocalVariable(x)` and `GlobalVariable(g)` are simply `x` and `g`. Such
1500    /// Naga expressions represent both WGSL pointers and references; it's the
1501    /// caller's responsibility to distinguish those cases appropriately.
1502    fn write_expr_plain_form(
1503        &mut self,
1504        module: &Module,
1505        expr: Handle<crate::Expression>,
1506        func_ctx: &back::FunctionCtx<'_>,
1507        indirection: Indirection,
1508    ) -> BackendResult {
1509        use crate::Expression;
1510
1511        if let Some(name) = self.named_expressions.get(&expr) {
1512            write!(self.out, "{name}")?;
1513            return Ok(());
1514        }
1515
1516        let expression = &func_ctx.expressions[expr];
1517
1518        // Write the plain WGSL form of a Naga expression.
1519        //
1520        // The plain form of `LocalVariable` and `GlobalVariable` expressions is
1521        // simply the variable name; `*` and `&` operators are never emitted.
1522        //
1523        // The plain form of `Access` and `AccessIndex` expressions are WGSL
1524        // `postfix_expression` forms for member/component access and
1525        // subscripting.
1526        match *expression {
1527            Expression::Literal(_)
1528            | Expression::Constant(_)
1529            | Expression::ZeroValue(_)
1530            | Expression::Compose { .. }
1531            | Expression::Splat { .. } => {
1532                self.write_possibly_const_expression(
1533                    module,
1534                    expr,
1535                    func_ctx.expressions,
1536                    |writer, expr| writer.write_expr(module, expr, func_ctx),
1537                )?;
1538            }
1539            Expression::Override(handle) => {
1540                write!(self.out, "{}", self.names[&NameKey::Override(handle)])?;
1541            }
1542            Expression::FunctionArgument(pos) => {
1543                let name_key = func_ctx.argument_key(pos);
1544                let name = &self.names[&name_key];
1545                write!(self.out, "{name}")?;
1546            }
1547            Expression::Binary { op, left, right } => {
1548                write!(self.out, "(")?;
1549                self.write_expr(module, left, func_ctx)?;
1550                write!(self.out, " {} ", back::binary_operation_str(op))?;
1551                self.write_expr(module, right, func_ctx)?;
1552                write!(self.out, ")")?;
1553            }
1554            Expression::Access { base, index } => {
1555                self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1556                write!(self.out, "[")?;
1557                self.write_expr(module, index, func_ctx)?;
1558                write!(self.out, "]")?
1559            }
1560            Expression::AccessIndex { base, index } => {
1561                let base_ty_res = &func_ctx.info[base].ty;
1562                let mut resolved = base_ty_res.inner_with(&module.types);
1563
1564                self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1565
1566                let base_ty_handle = match *resolved {
1567                    TypeInner::Pointer { base, space: _ } => {
1568                        resolved = &module.types[base].inner;
1569                        Some(base)
1570                    }
1571                    _ => base_ty_res.handle(),
1572                };
1573
1574                match *resolved {
1575                    TypeInner::Vector { .. } => {
1576                        // Write vector access as a swizzle
1577                        write!(self.out, ".{}", back::COMPONENTS[index as usize])?
1578                    }
1579                    TypeInner::Matrix { .. }
1580                    | TypeInner::Array { .. }
1581                    | TypeInner::BindingArray { .. }
1582                    | TypeInner::ValuePointer { .. } => write!(self.out, "[{index}]")?,
1583                    TypeInner::Struct { .. } => {
1584                        // This will never panic in case the type is a `Struct`, this is not true
1585                        // for other types so we can only check while inside this match arm
1586                        let ty = base_ty_handle.unwrap();
1587
1588                        write!(
1589                            self.out,
1590                            ".{}",
1591                            &self.names[&NameKey::StructMember(ty, index)]
1592                        )?
1593                    }
1594                    ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
1595                }
1596            }
1597            Expression::ImageSample {
1598                image,
1599                sampler,
1600                gather: None,
1601                coordinate,
1602                array_index,
1603                offset,
1604                level,
1605                depth_ref,
1606                clamp_to_edge,
1607            } => {
1608                use crate::SampleLevel as Sl;
1609
1610                let suffix_cmp = match depth_ref {
1611                    Some(_) => "Compare",
1612                    None => "",
1613                };
1614                let suffix_level = match level {
1615                    Sl::Auto => "",
1616                    Sl::Zero if clamp_to_edge => "BaseClampToEdge",
1617                    Sl::Zero | Sl::Exact(_) => "Level",
1618                    Sl::Bias(_) => "Bias",
1619                    Sl::Gradient { .. } => "Grad",
1620                };
1621
1622                write!(self.out, "textureSample{suffix_cmp}{suffix_level}(")?;
1623                self.write_expr(module, image, func_ctx)?;
1624                write!(self.out, ", ")?;
1625                self.write_expr(module, sampler, func_ctx)?;
1626                write!(self.out, ", ")?;
1627                self.write_expr(module, coordinate, func_ctx)?;
1628
1629                if let Some(array_index) = array_index {
1630                    write!(self.out, ", ")?;
1631                    self.write_expr(module, array_index, func_ctx)?;
1632                }
1633
1634                if let Some(depth_ref) = depth_ref {
1635                    write!(self.out, ", ")?;
1636                    self.write_expr(module, depth_ref, func_ctx)?;
1637                }
1638
1639                match level {
1640                    Sl::Auto => {}
1641                    Sl::Zero => {
1642                        // Level 0 is implied for depth comparison and BaseClampToEdge
1643                        if depth_ref.is_none() && !clamp_to_edge {
1644                            write!(self.out, ", 0.0")?;
1645                        }
1646                    }
1647                    Sl::Exact(expr) => {
1648                        write!(self.out, ", ")?;
1649                        self.write_expr(module, expr, func_ctx)?;
1650                    }
1651                    Sl::Bias(expr) => {
1652                        write!(self.out, ", ")?;
1653                        self.write_expr(module, expr, func_ctx)?;
1654                    }
1655                    Sl::Gradient { x, y } => {
1656                        write!(self.out, ", ")?;
1657                        self.write_expr(module, x, func_ctx)?;
1658                        write!(self.out, ", ")?;
1659                        self.write_expr(module, y, func_ctx)?;
1660                    }
1661                }
1662
1663                if let Some(offset) = offset {
1664                    write!(self.out, ", ")?;
1665                    self.write_const_expression(module, offset, func_ctx.expressions)?;
1666                }
1667
1668                write!(self.out, ")")?;
1669            }
1670
1671            Expression::ImageSample {
1672                image,
1673                sampler,
1674                gather: Some(component),
1675                coordinate,
1676                array_index,
1677                offset,
1678                level: _,
1679                depth_ref,
1680                clamp_to_edge: _,
1681            } => {
1682                let suffix_cmp = match depth_ref {
1683                    Some(_) => "Compare",
1684                    None => "",
1685                };
1686
1687                write!(self.out, "textureGather{suffix_cmp}(")?;
1688                match *func_ctx.resolve_type(image, &module.types) {
1689                    TypeInner::Image {
1690                        class: crate::ImageClass::Depth { multi: _ },
1691                        ..
1692                    } => {}
1693                    _ => {
1694                        write!(self.out, "{}, ", component as u8)?;
1695                    }
1696                }
1697                self.write_expr(module, image, func_ctx)?;
1698                write!(self.out, ", ")?;
1699                self.write_expr(module, sampler, func_ctx)?;
1700                write!(self.out, ", ")?;
1701                self.write_expr(module, coordinate, func_ctx)?;
1702
1703                if let Some(array_index) = array_index {
1704                    write!(self.out, ", ")?;
1705                    self.write_expr(module, array_index, func_ctx)?;
1706                }
1707
1708                if let Some(depth_ref) = depth_ref {
1709                    write!(self.out, ", ")?;
1710                    self.write_expr(module, depth_ref, func_ctx)?;
1711                }
1712
1713                if let Some(offset) = offset {
1714                    write!(self.out, ", ")?;
1715                    self.write_const_expression(module, offset, func_ctx.expressions)?;
1716                }
1717
1718                write!(self.out, ")")?;
1719            }
1720            Expression::ImageQuery { image, query } => {
1721                use crate::ImageQuery as Iq;
1722
1723                let texture_function = match query {
1724                    Iq::Size { .. } => "textureDimensions",
1725                    Iq::NumLevels => "textureNumLevels",
1726                    Iq::NumLayers => "textureNumLayers",
1727                    Iq::NumSamples => "textureNumSamples",
1728                };
1729
1730                write!(self.out, "{texture_function}(")?;
1731                self.write_expr(module, image, func_ctx)?;
1732                if let Iq::Size { level: Some(level) } = query {
1733                    write!(self.out, ", ")?;
1734                    self.write_expr(module, level, func_ctx)?;
1735                };
1736                write!(self.out, ")")?;
1737            }
1738
1739            Expression::ImageLoad {
1740                image,
1741                coordinate,
1742                array_index,
1743                sample,
1744                level,
1745            } => {
1746                write!(self.out, "textureLoad(")?;
1747                self.write_expr(module, image, func_ctx)?;
1748                write!(self.out, ", ")?;
1749                self.write_expr(module, coordinate, func_ctx)?;
1750                if let Some(array_index) = array_index {
1751                    write!(self.out, ", ")?;
1752                    self.write_expr(module, array_index, func_ctx)?;
1753                }
1754                if let Some(index) = sample.or(level) {
1755                    write!(self.out, ", ")?;
1756                    self.write_expr(module, index, func_ctx)?;
1757                }
1758                write!(self.out, ")")?;
1759            }
1760            Expression::GlobalVariable(handle) => {
1761                let name = &self.names[&NameKey::GlobalVariable(handle)];
1762                write!(self.out, "{name}")?;
1763            }
1764
1765            Expression::As {
1766                expr,
1767                kind,
1768                convert,
1769            } => {
1770                let inner = func_ctx.resolve_type(expr, &module.types);
1771                match *inner {
1772                    TypeInner::Matrix {
1773                        columns,
1774                        rows,
1775                        scalar,
1776                    } => {
1777                        let scalar = crate::Scalar {
1778                            kind,
1779                            width: convert.unwrap_or(scalar.width),
1780                        };
1781                        let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1782                        write!(
1783                            self.out,
1784                            "mat{}x{}<{}>",
1785                            common::vector_size_str(columns),
1786                            common::vector_size_str(rows),
1787                            scalar_kind_str
1788                        )?;
1789                    }
1790                    TypeInner::Vector {
1791                        size,
1792                        scalar: crate::Scalar { width, .. },
1793                    } => {
1794                        let scalar = crate::Scalar {
1795                            kind,
1796                            width: convert.unwrap_or(width),
1797                        };
1798                        let vector_size_str = common::vector_size_str(size);
1799                        let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1800                        if convert.is_some() {
1801                            write!(self.out, "vec{vector_size_str}<{scalar_kind_str}>")?;
1802                        } else {
1803                            write!(self.out, "bitcast<vec{vector_size_str}<{scalar_kind_str}>>")?;
1804                        }
1805                    }
1806                    TypeInner::Scalar(crate::Scalar { width, .. }) => {
1807                        let scalar = crate::Scalar {
1808                            kind,
1809                            width: convert.unwrap_or(width),
1810                        };
1811                        let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1812                        if convert.is_some() {
1813                            write!(self.out, "{scalar_kind_str}")?
1814                        } else {
1815                            write!(self.out, "bitcast<{scalar_kind_str}>")?
1816                        }
1817                    }
1818                    _ => {
1819                        return Err(Error::Unimplemented(format!(
1820                            "write_expr expression::as {inner:?}"
1821                        )));
1822                    }
1823                };
1824                write!(self.out, "(")?;
1825                self.write_expr(module, expr, func_ctx)?;
1826                write!(self.out, ")")?;
1827            }
1828            Expression::Load { pointer } => {
1829                let is_atomic_pointer = func_ctx
1830                    .resolve_type(pointer, &module.types)
1831                    .is_atomic_pointer(&module.types);
1832
1833                if is_atomic_pointer {
1834                    write!(self.out, "atomicLoad(")?;
1835                    self.write_expr(module, pointer, func_ctx)?;
1836                    write!(self.out, ")")?;
1837                } else {
1838                    self.write_expr_with_indirection(
1839                        module,
1840                        pointer,
1841                        func_ctx,
1842                        Indirection::Reference,
1843                    )?;
1844                }
1845            }
1846            Expression::LocalVariable(handle) => {
1847                write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
1848            }
1849            Expression::ArrayLength(expr) => {
1850                write!(self.out, "arrayLength(")?;
1851                self.write_expr(module, expr, func_ctx)?;
1852                write!(self.out, ")")?;
1853            }
1854
1855            Expression::Math {
1856                fun,
1857                arg,
1858                arg1,
1859                arg2,
1860                arg3,
1861            } => {
1862                use crate::MathFunction as Mf;
1863
1864                enum Function {
1865                    Regular(&'static str),
1866                    InversePolyfill(InversePolyfill),
1867                }
1868
1869                let function = match fun.try_to_wgsl() {
1870                    Some(name) => Function::Regular(name),
1871                    None => match fun {
1872                        Mf::Inverse => {
1873                            let ty = func_ctx.resolve_type(arg, &module.types);
1874                            let Some(overload) = InversePolyfill::find_overload(ty) else {
1875                                return Err(Error::unsupported("math function", fun));
1876                            };
1877
1878                            Function::InversePolyfill(overload)
1879                        }
1880                        _ => return Err(Error::unsupported("math function", fun)),
1881                    },
1882                };
1883
1884                match function {
1885                    Function::Regular(fun_name) => {
1886                        write!(self.out, "{fun_name}(")?;
1887                        self.write_expr(module, arg, func_ctx)?;
1888                        for arg in IntoIterator::into_iter([arg1, arg2, arg3]).flatten() {
1889                            write!(self.out, ", ")?;
1890                            self.write_expr(module, arg, func_ctx)?;
1891                        }
1892                        write!(self.out, ")")?
1893                    }
1894                    Function::InversePolyfill(inverse) => {
1895                        write!(self.out, "{}(", inverse.fun_name)?;
1896                        self.write_expr(module, arg, func_ctx)?;
1897                        write!(self.out, ")")?;
1898                        self.required_polyfills.insert(inverse);
1899                    }
1900                }
1901            }
1902
1903            Expression::Swizzle {
1904                size,
1905                vector,
1906                pattern,
1907            } => {
1908                self.write_expr(module, vector, func_ctx)?;
1909                write!(self.out, ".")?;
1910                for &sc in pattern[..size as usize].iter() {
1911                    self.out.write_char(back::COMPONENTS[sc as usize])?;
1912                }
1913            }
1914            Expression::Unary { op, expr } => {
1915                let unary = match op {
1916                    crate::UnaryOperator::Negate => "-",
1917                    crate::UnaryOperator::LogicalNot => "!",
1918                    crate::UnaryOperator::BitwiseNot => "~",
1919                };
1920
1921                write!(self.out, "{unary}(")?;
1922                self.write_expr(module, expr, func_ctx)?;
1923
1924                write!(self.out, ")")?
1925            }
1926
1927            Expression::Select {
1928                condition,
1929                accept,
1930                reject,
1931            } => {
1932                write!(self.out, "select(")?;
1933                self.write_expr(module, reject, func_ctx)?;
1934                write!(self.out, ", ")?;
1935                self.write_expr(module, accept, func_ctx)?;
1936                write!(self.out, ", ")?;
1937                self.write_expr(module, condition, func_ctx)?;
1938                write!(self.out, ")")?
1939            }
1940            Expression::Derivative { axis, ctrl, expr } => {
1941                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
1942                let op = match (axis, ctrl) {
1943                    (Axis::X, Ctrl::Coarse) => "dpdxCoarse",
1944                    (Axis::X, Ctrl::Fine) => "dpdxFine",
1945                    (Axis::X, Ctrl::None) => "dpdx",
1946                    (Axis::Y, Ctrl::Coarse) => "dpdyCoarse",
1947                    (Axis::Y, Ctrl::Fine) => "dpdyFine",
1948                    (Axis::Y, Ctrl::None) => "dpdy",
1949                    (Axis::Width, Ctrl::Coarse) => "fwidthCoarse",
1950                    (Axis::Width, Ctrl::Fine) => "fwidthFine",
1951                    (Axis::Width, Ctrl::None) => "fwidth",
1952                };
1953                write!(self.out, "{op}(")?;
1954                self.write_expr(module, expr, func_ctx)?;
1955                write!(self.out, ")")?
1956            }
1957            Expression::Relational { fun, argument } => {
1958                use crate::RelationalFunction as Rf;
1959
1960                let fun_name = match fun {
1961                    Rf::All => "all",
1962                    Rf::Any => "any",
1963                    _ => return Err(Error::UnsupportedRelationalFunction(fun)),
1964                };
1965                write!(self.out, "{fun_name}(")?;
1966
1967                self.write_expr(module, argument, func_ctx)?;
1968
1969                write!(self.out, ")")?
1970            }
1971            // Not supported yet
1972            Expression::RayQueryGetIntersection { .. }
1973            | Expression::RayQueryVertexPositions { .. } => unreachable!(),
1974            // Nothing to do here, since call expression already cached
1975            Expression::CallResult(_)
1976            | Expression::AtomicResult { .. }
1977            | Expression::RayQueryProceedResult
1978            | Expression::SubgroupBallotResult
1979            | Expression::SubgroupOperationResult { .. }
1980            | Expression::WorkGroupUniformLoadResult { .. } => {}
1981            Expression::CooperativeLoad {
1982                columns,
1983                rows,
1984                role,
1985                ref data,
1986            } => {
1987                let suffix = if data.row_major { "T" } else { "" };
1988                let scalar = func_ctx.info[data.pointer]
1989                    .ty
1990                    .inner_with(&module.types)
1991                    .pointer_base_type()
1992                    .unwrap()
1993                    .inner_with(&module.types)
1994                    .scalar()
1995                    .unwrap();
1996                write!(
1997                    self.out,
1998                    "coopLoad{suffix}<coop_mat{}x{}<{},{:?}>>(",
1999                    columns as u32,
2000                    rows as u32,
2001                    scalar.try_to_wgsl().unwrap(),
2002                    role,
2003                )?;
2004                self.write_expr(module, data.pointer, func_ctx)?;
2005                write!(self.out, ", ")?;
2006                self.write_expr(module, data.stride, func_ctx)?;
2007                write!(self.out, ")")?;
2008            }
2009            Expression::CooperativeMultiplyAdd { a, b, c } => {
2010                write!(self.out, "coopMultiplyAdd(")?;
2011                self.write_expr(module, a, func_ctx)?;
2012                write!(self.out, ", ")?;
2013                self.write_expr(module, b, func_ctx)?;
2014                write!(self.out, ", ")?;
2015                self.write_expr(module, c, func_ctx)?;
2016                write!(self.out, ")")?;
2017            }
2018        }
2019
2020        Ok(())
2021    }
2022
2023    /// Helper method used to write global variables
2024    /// # Notes
2025    /// Always adds a newline
2026    fn write_global(
2027        &mut self,
2028        module: &Module,
2029        global: &crate::GlobalVariable,
2030        handle: Handle<crate::GlobalVariable>,
2031    ) -> BackendResult {
2032        // Write group and binding attributes if present
2033        if let Some(ref binding) = global.binding {
2034            self.write_attributes_line(&[
2035                Attribute::Group(binding.group),
2036                Attribute::Binding(binding.binding),
2037            ])?;
2038        }
2039
2040        if global
2041            .memory_decorations
2042            .contains(crate::MemoryDecorations::COHERENT)
2043        {
2044            write!(self.out, "@coherent ")?;
2045        }
2046        if global
2047            .memory_decorations
2048            .contains(crate::MemoryDecorations::VOLATILE)
2049        {
2050            write!(self.out, "@volatile ")?;
2051        }
2052
2053        // First write global name and address space if supported
2054        write!(self.out, "var")?;
2055        let (address, maybe_access) = address_space_str(global.space);
2056        if let Some(space) = address {
2057            write!(self.out, "<{space}")?;
2058            if let Some(access) = maybe_access {
2059                write!(self.out, ", {access}")?;
2060            }
2061            write!(self.out, ">")?;
2062        }
2063        write!(
2064            self.out,
2065            " {}: ",
2066            &self.names[&NameKey::GlobalVariable(handle)]
2067        )?;
2068
2069        // Write global type
2070        self.write_type(module, global.ty)?;
2071
2072        // Write initializer
2073        if let Some(init) = global.init {
2074            write!(self.out, " = ")?;
2075            self.write_const_expression(module, init, &module.global_expressions)?;
2076        }
2077
2078        // End with semicolon
2079        writeln!(self.out, ";")?;
2080
2081        Ok(())
2082    }
2083
2084    /// Helper method used to write global constants
2085    ///
2086    /// # Notes
2087    /// Ends in a newline
2088    fn write_global_constant(
2089        &mut self,
2090        module: &Module,
2091        handle: Handle<crate::Constant>,
2092    ) -> BackendResult {
2093        let name = &self.names[&NameKey::Constant(handle)];
2094        // First write only constant name
2095        write!(self.out, "const {name}: ")?;
2096        self.write_type(module, module.constants[handle].ty)?;
2097        write!(self.out, " = ")?;
2098        let init = module.constants[handle].init;
2099        self.write_const_expression(module, init, &module.global_expressions)?;
2100        writeln!(self.out, ";")?;
2101
2102        Ok(())
2103    }
2104
2105    /// Helper method used to write overrides
2106    ///
2107    /// # Notes
2108    /// Ends in a newline
2109    fn write_override(
2110        &mut self,
2111        module: &Module,
2112        handle: Handle<crate::Override>,
2113    ) -> BackendResult {
2114        let override_ = &module.overrides[handle];
2115        let name = &self.names[&NameKey::Override(handle)];
2116
2117        // Write @id attribute if present
2118        if let Some(id) = override_.id {
2119            write!(self.out, "@id({id}) ")?;
2120        }
2121
2122        // Write override declaration
2123        write!(self.out, "override {name}: ")?;
2124        self.write_type(module, override_.ty)?;
2125
2126        // Write initializer if present
2127        if let Some(init) = override_.init {
2128            write!(self.out, " = ")?;
2129            self.write_const_expression(module, init, &module.global_expressions)?;
2130        }
2131
2132        writeln!(self.out, ";")?;
2133
2134        Ok(())
2135    }
2136
2137    // See https://github.com/rust-lang/rust-clippy/issues/4979.
2138    pub fn finish(self) -> W {
2139        self.out
2140    }
2141}
2142
2143struct WriterTypeContext<'m> {
2144    module: &'m Module,
2145    names: &'m crate::FastHashMap<NameKey, String>,
2146}
2147
2148impl TypeContext for WriterTypeContext<'_> {
2149    fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {
2150        &self.module.types[handle]
2151    }
2152
2153    fn type_name(&self, handle: Handle<crate::Type>) -> &str {
2154        self.names[&NameKey::Type(handle)].as_str()
2155    }
2156
2157    fn write_unnamed_struct<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
2158        unreachable!("the WGSL back end should always provide type handles");
2159    }
2160
2161    fn write_override<W: Write>(
2162        &self,
2163        handle: Handle<crate::Override>,
2164        out: &mut W,
2165    ) -> core::fmt::Result {
2166        write!(out, "{}", self.names[&NameKey::Override(handle)])
2167    }
2168
2169    fn write_non_wgsl_inner<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
2170        unreachable!("backends should only be passed validated modules");
2171    }
2172
2173    fn write_non_wgsl_scalar<W: Write>(&self, _: crate::Scalar, _: &mut W) -> core::fmt::Result {
2174        unreachable!("backends should only be passed validated modules");
2175    }
2176}
2177
2178fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
2179    match *binding {
2180        crate::Binding::BuiltIn(built_in) => {
2181            if let crate::BuiltIn::Position { invariant: true } = built_in {
2182                vec![Attribute::BuiltIn(built_in), Attribute::Invariant]
2183            } else {
2184                vec![Attribute::BuiltIn(built_in)]
2185            }
2186        }
2187        crate::Binding::Location {
2188            location,
2189            interpolation,
2190            sampling,
2191            blend_src: None,
2192            per_primitive,
2193        } => {
2194            let mut attrs = vec![
2195                Attribute::Location(location),
2196                Attribute::Interpolate(interpolation, sampling),
2197            ];
2198            if per_primitive {
2199                attrs.push(Attribute::PerPrimitive);
2200            }
2201            attrs
2202        }
2203        crate::Binding::Location {
2204            location,
2205            interpolation,
2206            sampling,
2207            blend_src: Some(blend_src),
2208            per_primitive,
2209        } => {
2210            let mut attrs = vec![
2211                Attribute::Location(location),
2212                Attribute::BlendSrc(blend_src),
2213                Attribute::Interpolate(interpolation, sampling),
2214            ];
2215            if per_primitive {
2216                attrs.push(Attribute::PerPrimitive);
2217            }
2218            attrs
2219        }
2220    }
2221}