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