Skip to main content

naga/front/wgsl/lower/
mod.rs

1use alloc::{
2    borrow::ToOwned,
3    boxed::Box,
4    format,
5    string::{String, ToString},
6    vec::Vec,
7};
8use core::{matches, num::NonZeroU32};
9
10use crate::front::wgsl::error::{Error, ExpectedToken, InvalidAssignmentType};
11use crate::front::wgsl::index::Index;
12use crate::front::wgsl::parse::directive::enable_extension::EnableExtensions;
13use crate::front::wgsl::parse::number::Number;
14use crate::front::wgsl::parse::{ast, conv};
15use crate::front::wgsl::Result;
16use crate::front::Typifier;
17use crate::{
18    common::wgsl::{TryToWgsl, TypeContext},
19    compact::KeepUnused,
20};
21use crate::{common::ForDebugWithTypes, proc::LayoutErrorInner};
22use crate::{ir, proc};
23use crate::{Arena, FastHashMap, FastIndexMap, Handle, Span};
24
25use construction::Constructor;
26use template_list::TemplateListIter;
27
28mod construction;
29mod conversion;
30mod template_list;
31
32/// Resolves the inner type of a given expression.
33///
34/// Expects a &mut [`ExpressionContext`] and a [`Handle<Expression>`].
35///
36/// Returns a &[`ir::TypeInner`].
37///
38/// Ideally, we would simply have a function that takes a `&mut ExpressionContext`
39/// and returns a `&TypeResolution`. Unfortunately, this leads the borrow checker
40/// to conclude that the mutable borrow lasts for as long as we are using the
41/// `&TypeResolution`, so we can't use the `ExpressionContext` for anything else -
42/// like, say, resolving another operand's type. Using a macro that expands to
43/// two separate calls, only the first of which needs a `&mut`,
44/// lets the borrow checker see that the mutable borrow is over.
45macro_rules! resolve_inner {
46    ($ctx:ident, $expr:expr) => {{
47        $ctx.grow_types($expr)?;
48        $ctx.typifier()[$expr].inner_with(&$ctx.module.types)
49    }};
50}
51pub(super) use resolve_inner;
52
53/// Resolves the inner types of two given expressions.
54///
55/// Expects a &mut [`ExpressionContext`] and two [`Handle<Expression>`]s.
56///
57/// Returns a tuple containing two &[`ir::TypeInner`].
58///
59/// See the documentation of [`resolve_inner!`] for why this macro is necessary.
60macro_rules! resolve_inner_binary {
61    ($ctx:ident, $left:expr, $right:expr) => {{
62        $ctx.grow_types($left)?;
63        $ctx.grow_types($right)?;
64        (
65            $ctx.typifier()[$left].inner_with(&$ctx.module.types),
66            $ctx.typifier()[$right].inner_with(&$ctx.module.types),
67        )
68    }};
69}
70
71/// Resolves the type of a given expression.
72///
73/// Expects a &mut [`ExpressionContext`] and a [`Handle<Expression>`].
74///
75/// Returns a &[`TypeResolution`].
76///
77/// See the documentation of [`resolve_inner!`] for why this macro is necessary.
78///
79/// [`TypeResolution`]: proc::TypeResolution
80macro_rules! resolve {
81    ($ctx:ident, $expr:expr) => {{
82        let expr = $expr;
83        $ctx.grow_types(expr)?;
84        &$ctx.typifier()[expr]
85    }};
86}
87pub(super) use resolve;
88
89/// State for constructing a `ir::Module`.
90pub struct GlobalContext<'source, 'temp, 'out> {
91    enable_extensions: EnableExtensions,
92
93    /// The `TranslationUnit`'s expressions arena.
94    ast_expressions: &'temp Arena<ast::Expression<'source>>,
95
96    // Naga IR values.
97    /// The map from the names of module-scope declarations to the Naga IR
98    /// `Handle`s we have built for them, owned by `Lowerer::lower`.
99    globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,
100
101    /// The module we're constructing.
102    module: &'out mut ir::Module,
103
104    const_typifier: &'temp mut Typifier,
105
106    layouter: &'temp mut proc::Layouter,
107
108    global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
109}
110
111impl<'source> GlobalContext<'source, '_, '_> {
112    const fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> {
113        ExpressionContext {
114            enable_extensions: self.enable_extensions,
115            ast_expressions: self.ast_expressions,
116            globals: self.globals,
117            module: self.module,
118            const_typifier: self.const_typifier,
119            layouter: self.layouter,
120            expr_type: ExpressionContextType::Constant(None),
121            global_expression_kind_tracker: self.global_expression_kind_tracker,
122        }
123    }
124
125    const fn as_override(&mut self) -> ExpressionContext<'source, '_, '_> {
126        ExpressionContext {
127            enable_extensions: self.enable_extensions,
128            ast_expressions: self.ast_expressions,
129            globals: self.globals,
130            module: self.module,
131            const_typifier: self.const_typifier,
132            layouter: self.layouter,
133            expr_type: ExpressionContextType::Override,
134            global_expression_kind_tracker: self.global_expression_kind_tracker,
135        }
136    }
137
138    fn ensure_type_exists(
139        &mut self,
140        name: Option<String>,
141        inner: ir::TypeInner,
142    ) -> Handle<ir::Type> {
143        self.module
144            .types
145            .insert(ir::Type { inner, name }, Span::UNDEFINED)
146    }
147}
148
149/// State for lowering a statement within a function.
150pub struct StatementContext<'source, 'temp, 'out> {
151    enable_extensions: EnableExtensions,
152
153    // WGSL AST values.
154    /// A reference to [`TranslationUnit::expressions`] for the translation unit
155    /// we're lowering.
156    ///
157    /// [`TranslationUnit::expressions`]: ast::TranslationUnit::expressions
158    ast_expressions: &'temp Arena<ast::Expression<'source>>,
159
160    // Naga IR values.
161    /// The map from the names of module-scope declarations to the Naga IR
162    /// `Handle`s we have built for them, owned by `Lowerer::lower`.
163    globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,
164
165    /// A map from each `ast::Local` handle to the Naga expression
166    /// we've built for it:
167    ///
168    /// - WGSL function arguments become Naga [`FunctionArgument`] expressions.
169    ///
170    /// - WGSL `var` declarations become Naga [`LocalVariable`] expressions.
171    ///
172    /// - WGSL `let` declararations become arbitrary Naga expressions.
173    ///
174    /// This always borrows the `local_table` local variable in
175    /// [`Lowerer::function`].
176    ///
177    /// [`LocalVariable`]: ir::Expression::LocalVariable
178    /// [`FunctionArgument`]: ir::Expression::FunctionArgument
179    local_table:
180        &'temp mut FastHashMap<Handle<ast::Local>, Declared<Typed<Handle<ir::Expression>>>>,
181
182    const_typifier: &'temp mut Typifier,
183    typifier: &'temp mut Typifier,
184    layouter: &'temp mut proc::Layouter,
185    function: &'out mut ir::Function,
186    /// Stores the names of expressions that are assigned in `let` statement
187    /// Also stores the spans of the names, for use in errors.
188    named_expressions: &'out mut FastIndexMap<Handle<ir::Expression>, (String, Span)>,
189    module: &'out mut ir::Module,
190
191    /// Which `Expression`s in `self.naga_expressions` are const expressions, in
192    /// the WGSL sense.
193    ///
194    /// According to the WGSL spec, a const expression must not refer to any
195    /// `let` declarations, even if those declarations' initializers are
196    /// themselves const expressions. So this tracker is not simply concerned
197    /// with the form of the expressions; it is also tracking whether WGSL says
198    /// we should consider them to be const. See the use of `force_non_const` in
199    /// the code for lowering `let` bindings.
200    local_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
201    global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
202}
203
204impl<'a, 'temp> StatementContext<'a, 'temp, '_> {
205    const fn as_const<'t>(
206        &'t mut self,
207        block: &'t mut ir::Block,
208        emitter: &'t mut proc::Emitter,
209    ) -> ExpressionContext<'a, 't, 't>
210    where
211        'temp: 't,
212    {
213        ExpressionContext {
214            enable_extensions: self.enable_extensions,
215            globals: self.globals,
216            ast_expressions: self.ast_expressions,
217            const_typifier: self.const_typifier,
218            layouter: self.layouter,
219            global_expression_kind_tracker: self.global_expression_kind_tracker,
220            module: self.module,
221            expr_type: ExpressionContextType::Constant(Some(LocalExpressionContext {
222                local_table: self.local_table,
223                function: self.function,
224                block,
225                emitter,
226                typifier: self.typifier,
227                local_expression_kind_tracker: self.local_expression_kind_tracker,
228            })),
229        }
230    }
231
232    const fn as_expression<'t>(
233        &'t mut self,
234        block: &'t mut ir::Block,
235        emitter: &'t mut proc::Emitter,
236    ) -> ExpressionContext<'a, 't, 't>
237    where
238        'temp: 't,
239    {
240        ExpressionContext {
241            enable_extensions: self.enable_extensions,
242            globals: self.globals,
243            ast_expressions: self.ast_expressions,
244            const_typifier: self.const_typifier,
245            layouter: self.layouter,
246            global_expression_kind_tracker: self.global_expression_kind_tracker,
247            module: self.module,
248            expr_type: ExpressionContextType::Runtime(LocalExpressionContext {
249                local_table: self.local_table,
250                function: self.function,
251                block,
252                emitter,
253                typifier: self.typifier,
254                local_expression_kind_tracker: self.local_expression_kind_tracker,
255            }),
256        }
257    }
258
259    #[allow(dead_code)]
260    const fn as_global(&mut self) -> GlobalContext<'a, '_, '_> {
261        GlobalContext {
262            enable_extensions: self.enable_extensions,
263            ast_expressions: self.ast_expressions,
264            globals: self.globals,
265            module: self.module,
266            const_typifier: self.const_typifier,
267            layouter: self.layouter,
268            global_expression_kind_tracker: self.global_expression_kind_tracker,
269        }
270    }
271
272    fn invalid_assignment_type(&self, expr: Handle<ir::Expression>) -> InvalidAssignmentType {
273        if let Some(&(_, span)) = self.named_expressions.get(&expr) {
274            InvalidAssignmentType::ImmutableBinding(span)
275        } else {
276            match self.function.expressions[expr] {
277                ir::Expression::Swizzle { .. } => InvalidAssignmentType::Swizzle,
278                ir::Expression::Access { base, .. } => self.invalid_assignment_type(base),
279                ir::Expression::AccessIndex { base, .. } => self.invalid_assignment_type(base),
280                _ => InvalidAssignmentType::Other,
281            }
282        }
283    }
284}
285
286pub struct LocalExpressionContext<'temp, 'out> {
287    /// A map from [`ast::Local`] handles to the Naga expressions we've built for them.
288    ///
289    /// This is always [`StatementContext::local_table`] for the
290    /// enclosing statement; see that documentation for details.
291    local_table: &'temp FastHashMap<Handle<ast::Local>, Declared<Typed<Handle<ir::Expression>>>>,
292
293    function: &'out mut ir::Function,
294    block: &'temp mut ir::Block,
295    emitter: &'temp mut proc::Emitter,
296    typifier: &'temp mut Typifier,
297
298    /// Which `Expression`s in `self.naga_expressions` are const expressions, in
299    /// the WGSL sense.
300    ///
301    /// See [`StatementContext::local_expression_kind_tracker`] for details.
302    local_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
303}
304
305/// The type of Naga IR expression we are lowering an [`ast::Expression`] to.
306pub enum ExpressionContextType<'temp, 'out> {
307    /// We are lowering to an arbitrary runtime expression, to be
308    /// included in a function's body.
309    ///
310    /// The given [`LocalExpressionContext`] holds information about local
311    /// variables, arguments, and other definitions available only to runtime
312    /// expressions, not constant or override expressions.
313    Runtime(LocalExpressionContext<'temp, 'out>),
314
315    /// We are lowering to a constant expression, to be included in the module's
316    /// constant expression arena.
317    ///
318    /// Everything global constant expressions are allowed to refer to is
319    /// available in the [`ExpressionContext`], but local constant expressions can
320    /// also refer to other
321    Constant(Option<LocalExpressionContext<'temp, 'out>>),
322
323    /// We are lowering to an override expression, to be included in the module's
324    /// constant expression arena.
325    ///
326    /// Everything override expressions are allowed to refer to is
327    /// available in the [`ExpressionContext`], so this variant
328    /// carries no further information.
329    Override,
330}
331
332/// State for lowering an [`ast::Expression`] to Naga IR.
333///
334/// [`ExpressionContext`]s come in two kinds, distinguished by
335/// the value of the [`expr_type`] field:
336///
337/// - A [`Runtime`] context contributes [`naga::Expression`]s to a [`naga::Function`]'s
338///   runtime expression arena.
339///
340/// - A [`Constant`] context contributes [`naga::Expression`]s to a [`naga::Module`]'s
341///   constant expression arena.
342///
343/// [`ExpressionContext`]s are constructed in restricted ways:
344///
345/// - To get a [`Runtime`] [`ExpressionContext`], call
346///   [`StatementContext::as_expression`].
347///
348/// - To get a [`Constant`] [`ExpressionContext`], call
349///   [`GlobalContext::as_const`].
350///
351/// - You can demote a [`Runtime`] context to a [`Constant`] context
352///   by calling [`as_const`], but there's no way to go in the other
353///   direction, producing a runtime context from a constant one. This
354///   is because runtime expressions can refer to constant
355///   expressions, via [`Expression::Constant`], but constant
356///   expressions can't refer to a function's expressions.
357///
358/// Not to be confused with `wgsl::parse::ExpressionContext`, which is
359/// for parsing the `ast::Expression` in the first place.
360///
361/// [`expr_type`]: ExpressionContext::expr_type
362/// [`Runtime`]: ExpressionContextType::Runtime
363/// [`naga::Expression`]: ir::Expression
364/// [`naga::Function`]: ir::Function
365/// [`Constant`]: ExpressionContextType::Constant
366/// [`naga::Module`]: ir::Module
367/// [`as_const`]: ExpressionContext::as_const
368/// [`Expression::Constant`]: ir::Expression::Constant
369pub struct ExpressionContext<'source, 'temp, 'out> {
370    enable_extensions: EnableExtensions,
371
372    // WGSL AST values.
373    ast_expressions: &'temp Arena<ast::Expression<'source>>,
374
375    // Naga IR values.
376    /// The map from the names of module-scope declarations to the Naga IR
377    /// `Handle`s we have built for them, owned by `Lowerer::lower`.
378    globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,
379
380    /// The IR [`Module`] we're constructing.
381    ///
382    /// [`Module`]: ir::Module
383    module: &'out mut ir::Module,
384
385    /// Type judgments for [`module::global_expressions`].
386    ///
387    /// [`module::global_expressions`]: ir::Module::global_expressions
388    const_typifier: &'temp mut Typifier,
389    layouter: &'temp mut proc::Layouter,
390    global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker,
391
392    /// Whether we are lowering a constant expression or a general
393    /// runtime expression, and the data needed in each case.
394    expr_type: ExpressionContextType<'temp, 'out>,
395}
396
397impl TypeContext for ExpressionContext<'_, '_, '_> {
398    fn lookup_type(&self, handle: Handle<ir::Type>) -> &ir::Type {
399        &self.module.types[handle]
400    }
401
402    fn type_name(&self, handle: Handle<ir::Type>) -> &str {
403        self.module.types[handle]
404            .name
405            .as_deref()
406            .unwrap_or("{anonymous type}")
407    }
408
409    fn write_override<W: core::fmt::Write>(
410        &self,
411        handle: Handle<ir::Override>,
412        out: &mut W,
413    ) -> core::fmt::Result {
414        match self.module.overrides[handle].name {
415            Some(ref name) => out.write_str(name),
416            None => write!(out, "{{anonymous override {handle:?}}}"),
417        }
418    }
419
420    fn write_unnamed_struct<W: core::fmt::Write>(
421        &self,
422        _: &ir::TypeInner,
423        _: &mut W,
424    ) -> core::fmt::Result {
425        unreachable!("the WGSL front end should always know the type name");
426    }
427}
428
429impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> {
430    const fn is_runtime(&self) -> bool {
431        match self.expr_type {
432            ExpressionContextType::Runtime(_) => true,
433            ExpressionContextType::Constant(_) | ExpressionContextType::Override => false,
434        }
435    }
436
437    #[allow(dead_code)]
438    const fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> {
439        ExpressionContext {
440            enable_extensions: self.enable_extensions,
441            globals: self.globals,
442            ast_expressions: self.ast_expressions,
443            const_typifier: self.const_typifier,
444            layouter: self.layouter,
445            module: self.module,
446            expr_type: ExpressionContextType::Constant(match self.expr_type {
447                ExpressionContextType::Runtime(ref mut local_expression_context)
448                | ExpressionContextType::Constant(Some(ref mut local_expression_context)) => {
449                    Some(LocalExpressionContext {
450                        local_table: local_expression_context.local_table,
451                        function: local_expression_context.function,
452                        block: local_expression_context.block,
453                        emitter: local_expression_context.emitter,
454                        typifier: local_expression_context.typifier,
455                        local_expression_kind_tracker: local_expression_context
456                            .local_expression_kind_tracker,
457                    })
458                }
459                ExpressionContextType::Constant(None) | ExpressionContextType::Override => None,
460            }),
461            global_expression_kind_tracker: self.global_expression_kind_tracker,
462        }
463    }
464
465    const fn as_global(&mut self) -> GlobalContext<'source, '_, '_> {
466        GlobalContext {
467            enable_extensions: self.enable_extensions,
468            ast_expressions: self.ast_expressions,
469            globals: self.globals,
470            module: self.module,
471            const_typifier: self.const_typifier,
472            layouter: self.layouter,
473            global_expression_kind_tracker: self.global_expression_kind_tracker,
474        }
475    }
476
477    const fn as_const_evaluator(&mut self) -> proc::ConstantEvaluator<'_> {
478        match self.expr_type {
479            ExpressionContextType::Runtime(ref mut rctx) => {
480                proc::ConstantEvaluator::for_wgsl_function(
481                    self.module,
482                    &mut rctx.function.expressions,
483                    rctx.local_expression_kind_tracker,
484                    self.layouter,
485                    rctx.emitter,
486                    rctx.block,
487                    false,
488                )
489            }
490            ExpressionContextType::Constant(Some(ref mut rctx)) => {
491                proc::ConstantEvaluator::for_wgsl_function(
492                    self.module,
493                    &mut rctx.function.expressions,
494                    rctx.local_expression_kind_tracker,
495                    self.layouter,
496                    rctx.emitter,
497                    rctx.block,
498                    true,
499                )
500            }
501            ExpressionContextType::Constant(None) => proc::ConstantEvaluator::for_wgsl_module(
502                self.module,
503                self.global_expression_kind_tracker,
504                self.layouter,
505                false,
506            ),
507            ExpressionContextType::Override => proc::ConstantEvaluator::for_wgsl_module(
508                self.module,
509                self.global_expression_kind_tracker,
510                self.layouter,
511                true,
512            ),
513        }
514    }
515
516    /// Return a wrapper around `value` suitable for formatting.
517    ///
518    /// Return a wrapper around `value` that implements
519    /// [`core::fmt::Display`] in a form suitable for use in
520    /// diagnostic messages.
521    const fn as_diagnostic_display<T>(
522        &self,
523        value: T,
524    ) -> crate::common::DiagnosticDisplay<(T, proc::GlobalCtx<'_>)> {
525        let ctx = self.module.to_ctx();
526        crate::common::DiagnosticDisplay((value, ctx))
527    }
528
529    fn append_expression(
530        &mut self,
531        expr: ir::Expression,
532        span: Span,
533    ) -> Result<'source, Handle<ir::Expression>> {
534        let mut eval = self.as_const_evaluator();
535        eval.try_eval_and_append(expr, span)
536            .map_err(|e| Box::new(Error::ConstantEvaluatorError(e.into(), span)))
537    }
538
539    fn get_const_val<T: TryFrom<crate::Literal, Error = proc::ConstValueError>>(
540        &self,
541        handle: Handle<ir::Expression>,
542    ) -> core::result::Result<T, proc::ConstValueError> {
543        match self.expr_type {
544            ExpressionContextType::Runtime(ref ctx) => {
545                if !ctx.local_expression_kind_tracker.is_const(handle) {
546                    return Err(proc::ConstValueError::NonConst);
547                }
548
549                self.module
550                    .to_ctx()
551                    .get_const_val_from(handle, &ctx.function.expressions)
552            }
553            ExpressionContextType::Constant(Some(ref ctx)) => {
554                assert!(ctx.local_expression_kind_tracker.is_const(handle));
555                self.module
556                    .to_ctx()
557                    .get_const_val_from(handle, &ctx.function.expressions)
558            }
559            ExpressionContextType::Constant(None) => self.module.to_ctx().get_const_val(handle),
560            ExpressionContextType::Override => Err(proc::ConstValueError::NonConst),
561        }
562    }
563
564    /// Return `true` if `handle` is a constant expression.
565    fn is_const(&self, handle: Handle<ir::Expression>) -> bool {
566        use ExpressionContextType as Ect;
567        match self.expr_type {
568            Ect::Runtime(ref ctx) | Ect::Constant(Some(ref ctx)) => {
569                ctx.local_expression_kind_tracker.is_const(handle)
570            }
571            Ect::Constant(None) | Ect::Override => {
572                self.global_expression_kind_tracker.is_const(handle)
573            }
574        }
575    }
576
577    fn get_expression_span(&self, handle: Handle<ir::Expression>) -> Span {
578        match self.expr_type {
579            ExpressionContextType::Runtime(ref ctx)
580            | ExpressionContextType::Constant(Some(ref ctx)) => {
581                ctx.function.expressions.get_span(handle)
582            }
583            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
584                self.module.global_expressions.get_span(handle)
585            }
586        }
587    }
588
589    const fn typifier(&self) -> &Typifier {
590        match self.expr_type {
591            ExpressionContextType::Runtime(ref ctx)
592            | ExpressionContextType::Constant(Some(ref ctx)) => ctx.typifier,
593            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
594                self.const_typifier
595            }
596        }
597    }
598
599    fn get(&self, handle: Handle<crate::Expression>) -> &crate::Expression {
600        match self.expr_type {
601            ExpressionContextType::Runtime(ref ctx)
602            | ExpressionContextType::Constant(Some(ref ctx)) => &ctx.function.expressions[handle],
603            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
604                &self.module.global_expressions[handle]
605            }
606        }
607    }
608
609    fn local(
610        &mut self,
611        local: &Handle<ast::Local>,
612        span: Span,
613    ) -> Result<'source, Typed<Handle<ir::Expression>>> {
614        match self.expr_type {
615            ExpressionContextType::Runtime(ref ctx) => Ok(ctx.local_table[local].runtime()),
616            ExpressionContextType::Constant(Some(ref ctx)) => ctx.local_table[local]
617                .const_time()
618                .ok_or(Box::new(Error::UnexpectedOperationInConstContext(span))),
619            _ => Err(Box::new(Error::UnexpectedOperationInConstContext(span))),
620        }
621    }
622
623    fn runtime_expression_ctx(
624        &mut self,
625        span: Span,
626    ) -> Result<'source, &mut LocalExpressionContext<'temp, 'out>> {
627        match self.expr_type {
628            ExpressionContextType::Runtime(ref mut ctx) => Ok(ctx),
629            ExpressionContextType::Constant(_) | ExpressionContextType::Override => {
630                Err(Box::new(Error::UnexpectedOperationInConstContext(span)))
631            }
632        }
633    }
634
635    fn with_nested_runtime_expression_ctx<'a, F, T>(
636        &mut self,
637        span: Span,
638        f: F,
639    ) -> Result<'source, (T, crate::Block)>
640    where
641        for<'t> F: FnOnce(&mut ExpressionContext<'source, 't, 't>) -> Result<'source, T>,
642    {
643        let mut block = crate::Block::new();
644        let rctx = match self.expr_type {
645            ExpressionContextType::Runtime(ref mut rctx) => Ok(rctx),
646            ExpressionContextType::Constant(_) | ExpressionContextType::Override => {
647                Err(Error::UnexpectedOperationInConstContext(span))
648            }
649        }?;
650
651        rctx.block
652            .extend(rctx.emitter.finish(&rctx.function.expressions));
653        rctx.emitter.start(&rctx.function.expressions);
654
655        let nested_rctx = LocalExpressionContext {
656            local_table: rctx.local_table,
657            function: rctx.function,
658            block: &mut block,
659            emitter: rctx.emitter,
660            typifier: rctx.typifier,
661            local_expression_kind_tracker: rctx.local_expression_kind_tracker,
662        };
663        let mut nested_ctx = ExpressionContext {
664            enable_extensions: self.enable_extensions,
665            expr_type: ExpressionContextType::Runtime(nested_rctx),
666            ast_expressions: self.ast_expressions,
667            globals: self.globals,
668            module: self.module,
669            const_typifier: self.const_typifier,
670            layouter: self.layouter,
671            global_expression_kind_tracker: self.global_expression_kind_tracker,
672        };
673        let ret = f(&mut nested_ctx)?;
674
675        block.extend(rctx.emitter.finish(&rctx.function.expressions));
676        rctx.emitter.start(&rctx.function.expressions);
677
678        Ok((ret, block))
679    }
680
681    fn gather_component(
682        &mut self,
683        expr: Handle<ir::Expression>,
684        component_span: Span,
685        gather_span: Span,
686    ) -> Result<'source, ir::SwizzleComponent> {
687        match self.expr_type {
688            ExpressionContextType::Runtime(ref rctx) => {
689                if !rctx.local_expression_kind_tracker.is_const(expr) {
690                    return Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
691                        component_span,
692                    )));
693                }
694
695                let index = self
696                    .module
697                    .to_ctx()
698                    .get_const_val_from::<u32, _>(expr, &rctx.function.expressions)
699                    .map_err(|err| match err {
700                        proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
701                            Error::ExpectedConstExprConcreteIntegerScalar(component_span)
702                        }
703                        proc::ConstValueError::Negative => {
704                            Error::ExpectedNonNegative(component_span)
705                        }
706                    })?;
707                ir::SwizzleComponent::XYZW
708                    .get(index as usize)
709                    .copied()
710                    .ok_or(Box::new(Error::InvalidGatherComponent(component_span)))
711            }
712            // This means a `gather` operation appeared in a constant expression.
713            // This error refers to the `gather` itself, not its "component" argument.
714            ExpressionContextType::Constant(_) | ExpressionContextType::Override => Err(Box::new(
715                Error::UnexpectedOperationInConstContext(gather_span),
716            )),
717        }
718    }
719
720    /// Determine the type of `handle`, and add it to the module's arena.
721    ///
722    /// If you just need a `TypeInner` for `handle`'s type, use the
723    /// [`resolve_inner!`] macro instead. This function
724    /// should only be used when the type of `handle` needs to appear
725    /// in the module's final `Arena<Type>`, for example, if you're
726    /// creating a [`LocalVariable`] whose type is inferred from its
727    /// initializer.
728    ///
729    /// [`LocalVariable`]: ir::LocalVariable
730    fn register_type(
731        &mut self,
732        handle: Handle<ir::Expression>,
733    ) -> Result<'source, Handle<ir::Type>> {
734        self.grow_types(handle)?;
735        // This is equivalent to calling ExpressionContext::typifier(),
736        // except that this lets the borrow checker see that it's okay
737        // to also borrow self.module.types mutably below.
738        let typifier = match self.expr_type {
739            ExpressionContextType::Runtime(ref ctx)
740            | ExpressionContextType::Constant(Some(ref ctx)) => ctx.typifier,
741            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
742                &*self.const_typifier
743            }
744        };
745        Ok(typifier.register_type(handle, &mut self.module.types))
746    }
747
748    /// Resolve the types of all expressions up through `handle`.
749    ///
750    /// Ensure that [`self.typifier`] has a [`TypeResolution`] for
751    /// every expression in `self.function.expressions`.
752    ///
753    /// This does not add types to any arena. The [`Typifier`]
754    /// documentation explains the steps we take to avoid filling
755    /// arenas with intermediate types.
756    ///
757    /// This function takes `&mut self`, so it can't conveniently
758    /// return a shared reference to the resulting `TypeResolution`:
759    /// the shared reference would extend the mutable borrow, and you
760    /// wouldn't be able to use `self` for anything else. Instead, you
761    /// should use [`register_type`] or one of [`resolve!`],
762    /// [`resolve_inner!`] or [`resolve_inner_binary!`].
763    ///
764    /// [`self.typifier`]: ExpressionContext::typifier
765    /// [`TypeResolution`]: proc::TypeResolution
766    /// [`register_type`]: Self::register_type
767    /// [`Typifier`]: Typifier
768    fn grow_types(&mut self, handle: Handle<ir::Expression>) -> Result<'source, &mut Self> {
769        let empty_arena = Arena::new();
770        let resolve_ctx;
771        let typifier;
772        let expressions;
773        match self.expr_type {
774            ExpressionContextType::Runtime(ref mut ctx)
775            | ExpressionContextType::Constant(Some(ref mut ctx)) => {
776                resolve_ctx = proc::ResolveContext::with_locals(
777                    self.module,
778                    &ctx.function.local_variables,
779                    &ctx.function.arguments,
780                );
781                typifier = &mut *ctx.typifier;
782                expressions = &ctx.function.expressions;
783            }
784            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {
785                resolve_ctx = proc::ResolveContext::with_locals(self.module, &empty_arena, &[]);
786                typifier = self.const_typifier;
787                expressions = &self.module.global_expressions;
788            }
789        };
790        typifier
791            .grow(handle, expressions, &resolve_ctx)
792            .map_err(Error::InvalidResolve)?;
793
794        Ok(self)
795    }
796
797    fn image_data(
798        &mut self,
799        image: Handle<ir::Expression>,
800        span: Span,
801    ) -> Result<'source, (ir::ImageClass, bool)> {
802        match *resolve_inner!(self, image) {
803            ir::TypeInner::Image { class, arrayed, .. } => Ok((class, arrayed)),
804            _ => Err(Box::new(Error::BadTexture(span))),
805        }
806    }
807
808    fn prepare_args<'b>(
809        &mut self,
810        args: &'b [Handle<ast::Expression<'source>>],
811        min_args: u32,
812        span: Span,
813    ) -> ArgumentContext<'b, 'source> {
814        ArgumentContext {
815            args: args.iter(),
816            min_args,
817            args_used: 0,
818            total_args: args.len() as u32,
819            span,
820        }
821    }
822
823    /// Insert splats, if needed by the non-'*' operations.
824    ///
825    /// See the "Binary arithmetic expressions with mixed scalar and vector operands"
826    /// table in the WebGPU Shading Language specification for relevant operators.
827    ///
828    /// Multiply is not handled here as backends are expected to handle vec*scalar
829    /// operations, so inserting splats into the IR increases size needlessly.
830    fn binary_op_splat(
831        &mut self,
832        op: ir::BinaryOperator,
833        left: &mut Handle<ir::Expression>,
834        right: &mut Handle<ir::Expression>,
835    ) -> Result<'source, ()> {
836        if matches!(
837            op,
838            ir::BinaryOperator::Add
839                | ir::BinaryOperator::Subtract
840                | ir::BinaryOperator::Divide
841                | ir::BinaryOperator::Modulo
842        ) {
843            match resolve_inner_binary!(self, *left, *right) {
844                (&ir::TypeInner::Vector { size, .. }, &ir::TypeInner::Scalar { .. }) => {
845                    *right = self.append_expression(
846                        ir::Expression::Splat {
847                            size,
848                            value: *right,
849                        },
850                        self.get_expression_span(*right),
851                    )?;
852                }
853                (&ir::TypeInner::Scalar { .. }, &ir::TypeInner::Vector { size, .. }) => {
854                    *left = self.append_expression(
855                        ir::Expression::Splat { size, value: *left },
856                        self.get_expression_span(*left),
857                    )?;
858                }
859                _ => {}
860            }
861        }
862
863        Ok(())
864    }
865
866    /// Add a single expression to the expression table that is not covered by `self.emitter`.
867    ///
868    /// This is useful for `CallResult` and `AtomicResult` expressions, which should not be covered by
869    /// `Emit` statements.
870    fn interrupt_emitter(
871        &mut self,
872        expression: ir::Expression,
873        span: Span,
874    ) -> Result<'source, Handle<ir::Expression>> {
875        match self.expr_type {
876            ExpressionContextType::Runtime(ref mut rctx)
877            | ExpressionContextType::Constant(Some(ref mut rctx)) => {
878                rctx.block
879                    .extend(rctx.emitter.finish(&rctx.function.expressions));
880            }
881            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {}
882        }
883        let result = self.append_expression(expression, span);
884        match self.expr_type {
885            ExpressionContextType::Runtime(ref mut rctx)
886            | ExpressionContextType::Constant(Some(ref mut rctx)) => {
887                rctx.emitter.start(&rctx.function.expressions);
888            }
889            ExpressionContextType::Constant(None) | ExpressionContextType::Override => {}
890        }
891        result
892    }
893
894    /// Apply the WGSL Load Rule to `expr`.
895    ///
896    /// If `expr` is has type `ref<SC, T, A>`, perform a load to produce a value of type
897    /// `T`. Otherwise, return `expr` unchanged.
898    fn apply_load_rule(
899        &mut self,
900        expr: Typed<Handle<ir::Expression>>,
901    ) -> Result<'source, Handle<ir::Expression>> {
902        match expr {
903            Typed::Reference(pointer) => {
904                let span = self.get_expression_span(pointer);
905
906                // Reject direct access to atomic variables that does not go
907                // through a built-in function.
908                if resolve_inner!(self, pointer).is_atomic_pointer(&self.module.types) {
909                    return Err(Box::new(Error::InvalidAtomicAccess(span)));
910                }
911
912                let load = ir::Expression::Load { pointer };
913                self.append_expression(load, span)
914            }
915            Typed::Plain(handle) => Ok(handle),
916        }
917    }
918
919    fn ensure_type_exists(&mut self, inner: ir::TypeInner) -> Handle<ir::Type> {
920        self.as_global().ensure_type_exists(None, inner)
921    }
922
923    /// Check that `expr` is an identifier resolving to a predeclared enumerant.
924    ///
925    /// The identifier must not have any template parameters.
926    ///
927    /// Return the name of the identifier, together with its span.
928    ///
929    /// Actually, this only checks that the identifier refers to some
930    /// predeclared object, not necessarily an enumerant. This should be good
931    /// enough, since the caller is going to compare the name against some list
932    /// of permitted enumerants anyway.
933    fn enumerant(
934        &self,
935        expr: Handle<ast::Expression<'source>>,
936    ) -> Result<'source, (&'source str, Span)> {
937        let span = self.ast_expressions.get_span(expr);
938        let expr = &self.ast_expressions[expr];
939
940        let ast::Expression::Ident(ref ident) = *expr else {
941            return Err(Box::new(Error::UnexpectedExprForEnumerant(span)));
942        };
943
944        let ast::TemplateElaboratedIdent {
945            ident: ast::IdentExpr::Unresolved(name),
946            ref template_list,
947            ..
948        } = *ident
949        else {
950            return Err(Box::new(Error::UnexpectedIdentForEnumerant(span)));
951        };
952
953        if self.globals.get(name).is_some() {
954            return Err(Box::new(Error::UnexpectedIdentForEnumerant(span)));
955        }
956
957        if !template_list.is_empty() {
958            return Err(Box::new(Error::UnexpectedTemplate(span)));
959        }
960
961        Ok((name, span))
962    }
963
964    fn var_address_space(
965        &self,
966        template_list: &[Handle<ast::Expression<'source>>],
967    ) -> Result<'source, ir::AddressSpace> {
968        let mut tl = TemplateListIter::new(Span::UNDEFINED, template_list);
969        let mut address_space = tl.maybe_address_space(self)?;
970        if let Some(ref mut address_space) = address_space {
971            tl.maybe_access_mode(address_space, self)?;
972        }
973        tl.finish(self)?;
974        Ok(address_space.unwrap_or(ir::AddressSpace::Handle))
975    }
976}
977
978struct ArgumentContext<'ctx, 'source> {
979    args: core::slice::Iter<'ctx, Handle<ast::Expression<'source>>>,
980    min_args: u32,
981    args_used: u32,
982    total_args: u32,
983    span: Span,
984}
985
986impl<'source> ArgumentContext<'_, 'source> {
987    pub fn finish(self) -> Result<'source, ()> {
988        if self.args.len() == 0 {
989            Ok(())
990        } else {
991            Err(Box::new(Error::WrongArgumentCount {
992                found: self.total_args,
993                expected: self.min_args..self.args_used + 1,
994                span: self.span,
995            }))
996        }
997    }
998
999    pub fn next(&mut self) -> Result<'source, Handle<ast::Expression<'source>>> {
1000        match self.args.next().copied() {
1001            Some(arg) => {
1002                self.args_used += 1;
1003                Ok(arg)
1004            }
1005            None => Err(Box::new(Error::WrongArgumentCount {
1006                found: self.total_args,
1007                expected: self.min_args..self.args_used + 1,
1008                span: self.span,
1009            })),
1010        }
1011    }
1012}
1013
1014#[derive(Debug, Copy, Clone)]
1015enum Declared<T> {
1016    /// Value declared as const
1017    Const(T),
1018
1019    /// Value declared as non-const
1020    Runtime(T),
1021}
1022
1023impl<T> Declared<T> {
1024    fn runtime(self) -> T {
1025        match self {
1026            Declared::Const(t) | Declared::Runtime(t) => t,
1027        }
1028    }
1029
1030    fn const_time(self) -> Option<T> {
1031        match self {
1032            Declared::Const(t) => Some(t),
1033            Declared::Runtime(_) => None,
1034        }
1035    }
1036}
1037
1038/// WGSL type annotations on expressions, types, values, etc.
1039///
1040/// Naga and WGSL types are very close, but Naga lacks WGSL's `ref` types, which
1041/// we need to know to apply the Load Rule. This enum carries some WGSL or Naga
1042/// datum along with enough information to determine its corresponding WGSL
1043/// type.
1044///
1045/// The `T` type parameter can be any expression-like thing:
1046///
1047/// - `Typed<Handle<ir::Type>>` can represent a full WGSL type. For example,
1048///   given some Naga `Pointer` type `ptr`, a WGSL reference type is a
1049///   `Typed::Reference(ptr)` whereas a WGSL pointer type is a
1050///   `Typed::Plain(ptr)`.
1051///
1052/// - `Typed<ir::Expression>` or `Typed<Handle<ir::Expression>>` can
1053///   represent references similarly.
1054///
1055/// Use the `map` and `try_map` methods to convert from one expression
1056/// representation to another.
1057///
1058/// [`Expression`]: ir::Expression
1059#[derive(Debug, Copy, Clone)]
1060enum Typed<T> {
1061    /// A WGSL reference.
1062    Reference(T),
1063
1064    /// A WGSL plain type.
1065    Plain(T),
1066}
1067
1068impl<T> Typed<T> {
1069    fn map<U>(self, mut f: impl FnMut(T) -> U) -> Typed<U> {
1070        match self {
1071            Self::Reference(v) => Typed::Reference(f(v)),
1072            Self::Plain(v) => Typed::Plain(f(v)),
1073        }
1074    }
1075
1076    fn try_map<U, E>(
1077        self,
1078        mut f: impl FnMut(T) -> core::result::Result<U, E>,
1079    ) -> core::result::Result<Typed<U>, E> {
1080        Ok(match self {
1081            Self::Reference(expr) => Typed::Reference(f(expr)?),
1082            Self::Plain(expr) => Typed::Plain(f(expr)?),
1083        })
1084    }
1085
1086    fn ref_or<E>(self, error: E) -> core::result::Result<T, E> {
1087        match self {
1088            Self::Reference(v) => Ok(v),
1089            Self::Plain(_) => Err(error),
1090        }
1091    }
1092}
1093
1094/// A single vector component or swizzle.
1095///
1096/// This represents the things that can appear after the `.` in a vector access
1097/// expression: either a single component name, or a series of them,
1098/// representing a swizzle.
1099enum Components {
1100    Single(u32),
1101    Swizzle {
1102        size: ir::VectorSize,
1103        pattern: [ir::SwizzleComponent; 4],
1104    },
1105}
1106
1107impl Components {
1108    const fn letter_component(letter: char) -> Option<ir::SwizzleComponent> {
1109        use ir::SwizzleComponent as Sc;
1110        match letter {
1111            'x' | 'r' => Some(Sc::X),
1112            'y' | 'g' => Some(Sc::Y),
1113            'z' | 'b' => Some(Sc::Z),
1114            'w' | 'a' => Some(Sc::W),
1115            _ => None,
1116        }
1117    }
1118
1119    fn single_component(name: &str, name_span: Span) -> Result<'_, u32> {
1120        let ch = name.chars().next().ok_or(Error::BadAccessor(name_span))?;
1121        match Self::letter_component(ch) {
1122            Some(sc) => Ok(sc as u32),
1123            None => Err(Box::new(Error::BadAccessor(name_span))),
1124        }
1125    }
1126
1127    /// Construct a `Components` value from a 'member' name, like `"wzy"` or `"x"`.
1128    ///
1129    /// Use `name_span` for reporting errors in parsing the component string.
1130    fn new(name: &str, name_span: Span) -> Result<'_, Self> {
1131        let size = match name.len() {
1132            1 => return Ok(Components::Single(Self::single_component(name, name_span)?)),
1133            2 => ir::VectorSize::Bi,
1134            3 => ir::VectorSize::Tri,
1135            4 => ir::VectorSize::Quad,
1136            _ => return Err(Box::new(Error::BadAccessor(name_span))),
1137        };
1138
1139        let mut pattern = [ir::SwizzleComponent::X; 4];
1140        for (comp, ch) in pattern.iter_mut().zip(name.chars()) {
1141            *comp = Self::letter_component(ch).ok_or(Error::BadAccessor(name_span))?;
1142        }
1143
1144        if name.chars().all(|c| matches!(c, 'x' | 'y' | 'z' | 'w'))
1145            || name.chars().all(|c| matches!(c, 'r' | 'g' | 'b' | 'a'))
1146        {
1147            Ok(Components::Swizzle { size, pattern })
1148        } else {
1149            Err(Box::new(Error::BadAccessor(name_span)))
1150        }
1151    }
1152}
1153
1154/// An `ast::GlobalDecl` for which we have built the Naga IR equivalent.
1155enum LoweredGlobalDecl {
1156    Function {
1157        handle: Handle<ir::Function>,
1158        must_use: bool,
1159    },
1160    Var(Handle<ir::GlobalVariable>),
1161    Const(Handle<ir::Constant>),
1162    Override(Handle<ir::Override>),
1163    Type(Handle<ir::Type>),
1164    EntryPoint(usize),
1165}
1166
1167enum Texture {
1168    Gather,
1169    GatherCompare,
1170
1171    Sample,
1172    SampleBias,
1173    SampleCompare,
1174    SampleCompareLevel,
1175    SampleGrad,
1176    SampleLevel,
1177    SampleBaseClampToEdge,
1178}
1179
1180impl Texture {
1181    pub fn map(word: &str) -> Option<Self> {
1182        Some(match word {
1183            "textureGather" => Self::Gather,
1184            "textureGatherCompare" => Self::GatherCompare,
1185
1186            "textureSample" => Self::Sample,
1187            "textureSampleBias" => Self::SampleBias,
1188            "textureSampleCompare" => Self::SampleCompare,
1189            "textureSampleCompareLevel" => Self::SampleCompareLevel,
1190            "textureSampleGrad" => Self::SampleGrad,
1191            "textureSampleLevel" => Self::SampleLevel,
1192            "textureSampleBaseClampToEdge" => Self::SampleBaseClampToEdge,
1193            _ => return None,
1194        })
1195    }
1196
1197    pub const fn min_argument_count(&self) -> u32 {
1198        match *self {
1199            Self::Gather => 3,
1200            Self::GatherCompare => 4,
1201
1202            Self::Sample => 3,
1203            Self::SampleBias => 5,
1204            Self::SampleCompare => 5,
1205            Self::SampleCompareLevel => 5,
1206            Self::SampleGrad => 6,
1207            Self::SampleLevel => 5,
1208            Self::SampleBaseClampToEdge => 3,
1209        }
1210    }
1211}
1212
1213enum SubgroupGather {
1214    BroadcastFirst,
1215    Broadcast,
1216    Shuffle,
1217    ShuffleDown,
1218    ShuffleUp,
1219    ShuffleXor,
1220    QuadBroadcast,
1221}
1222
1223impl SubgroupGather {
1224    pub fn map(word: &str) -> Option<Self> {
1225        Some(match word {
1226            "subgroupBroadcastFirst" => Self::BroadcastFirst,
1227            "subgroupBroadcast" => Self::Broadcast,
1228            "subgroupShuffle" => Self::Shuffle,
1229            "subgroupShuffleDown" => Self::ShuffleDown,
1230            "subgroupShuffleUp" => Self::ShuffleUp,
1231            "subgroupShuffleXor" => Self::ShuffleXor,
1232            "quadBroadcast" => Self::QuadBroadcast,
1233            _ => return None,
1234        })
1235    }
1236}
1237
1238/// Whether a declaration accepts abstract types, or concretizes.
1239enum AbstractRule {
1240    /// This declaration concretizes its initialization expression.
1241    Concretize,
1242
1243    /// This declaration can accept initializers with abstract types.
1244    Allow,
1245}
1246
1247/// Whether `@must_use` applies to a call expression.
1248#[derive(Debug, Copy, Clone)]
1249enum MustUse {
1250    Yes,
1251    No,
1252}
1253
1254impl From<bool> for MustUse {
1255    fn from(value: bool) -> Self {
1256        if value {
1257            MustUse::Yes
1258        } else {
1259            MustUse::No
1260        }
1261    }
1262}
1263
1264pub struct Lowerer<'source, 'temp> {
1265    index: &'temp Index<'source>,
1266}
1267
1268impl<'source, 'temp> Lowerer<'source, 'temp> {
1269    pub const fn new(index: &'temp Index<'source>) -> Self {
1270        Self { index }
1271    }
1272
1273    pub fn lower(&mut self, tu: ast::TranslationUnit<'source>) -> Result<'source, ir::Module> {
1274        let mut module = ir::Module {
1275            diagnostic_filters: tu.diagnostic_filters,
1276            diagnostic_filter_leaf: tu.diagnostic_filter_leaf,
1277            ..Default::default()
1278        };
1279
1280        let mut ctx = GlobalContext {
1281            enable_extensions: tu.enable_extensions,
1282            ast_expressions: &tu.expressions,
1283            globals: &mut FastHashMap::default(),
1284            module: &mut module,
1285            const_typifier: &mut Typifier::new(),
1286            layouter: &mut proc::Layouter::default(),
1287            global_expression_kind_tracker: &mut proc::ExpressionKindTracker::new(),
1288        };
1289        if !tu.doc_comments.is_empty() {
1290            ctx.module.get_or_insert_default_doc_comments().module =
1291                tu.doc_comments.iter().map(|s| s.to_string()).collect();
1292        }
1293
1294        for decl_handle in self.index.visit_ordered() {
1295            let span = tu.decls.get_span(decl_handle);
1296            let decl = &tu.decls[decl_handle];
1297
1298            match decl.kind {
1299                ast::GlobalDeclKind::Fn(ref f) => {
1300                    let lowered_decl = self.function(f, span, &mut ctx)?;
1301                    if !f.doc_comments.is_empty() {
1302                        match lowered_decl {
1303                            LoweredGlobalDecl::Function { handle, .. } => {
1304                                ctx.module
1305                                    .get_or_insert_default_doc_comments()
1306                                    .functions
1307                                    .insert(
1308                                        handle,
1309                                        f.doc_comments.iter().map(|s| s.to_string()).collect(),
1310                                    );
1311                            }
1312                            LoweredGlobalDecl::EntryPoint(index) => {
1313                                ctx.module
1314                                    .get_or_insert_default_doc_comments()
1315                                    .entry_points
1316                                    .insert(
1317                                        index,
1318                                        f.doc_comments.iter().map(|s| s.to_string()).collect(),
1319                                    );
1320                            }
1321                            _ => {}
1322                        }
1323                    }
1324                    ctx.globals.insert(f.name.name, lowered_decl);
1325                }
1326                ast::GlobalDeclKind::Var(ref v) => {
1327                    let explicit_ty =
1328                        v.ty.as_ref()
1329                            .map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))
1330                            .transpose()?;
1331
1332                    let (ty, initializer) = self.type_and_init(
1333                        v.name,
1334                        v.init,
1335                        explicit_ty,
1336                        AbstractRule::Concretize,
1337                        &mut ctx.as_override(),
1338                    )?;
1339
1340                    let binding = if let Some(ref binding) = v.binding {
1341                        Some(ir::ResourceBinding {
1342                            group: self.const_u32(binding.group, &mut ctx.as_const())?.0,
1343                            binding: self.const_u32(binding.binding, &mut ctx.as_const())?.0,
1344                        })
1345                    } else {
1346                        None
1347                    };
1348
1349                    let space = ctx.as_const().var_address_space(&v.template_list)?;
1350
1351                    let handle = ctx.module.global_variables.append(
1352                        ir::GlobalVariable {
1353                            name: Some(v.name.name.to_string()),
1354                            space,
1355                            binding,
1356                            ty,
1357                            init: initializer,
1358                            memory_decorations: v.memory_decorations,
1359                        },
1360                        span,
1361                    );
1362
1363                    if !v.doc_comments.is_empty() {
1364                        ctx.module
1365                            .get_or_insert_default_doc_comments()
1366                            .global_variables
1367                            .insert(
1368                                handle,
1369                                v.doc_comments.iter().map(|s| s.to_string()).collect(),
1370                            );
1371                    }
1372                    ctx.globals
1373                        .insert(v.name.name, LoweredGlobalDecl::Var(handle));
1374                }
1375                ast::GlobalDeclKind::Const(ref c) => {
1376                    let mut ectx = ctx.as_const();
1377
1378                    let explicit_ty =
1379                        c.ty.as_ref()
1380                            .map(|ast| self.resolve_ast_type(ast, &mut ectx))
1381                            .transpose()?;
1382
1383                    let (ty, init) = self.type_and_init(
1384                        c.name,
1385                        Some(c.init),
1386                        explicit_ty,
1387                        AbstractRule::Allow,
1388                        &mut ectx,
1389                    )?;
1390                    let init = init.expect("Global const must have init");
1391
1392                    let handle = ctx.module.constants.append(
1393                        ir::Constant {
1394                            name: Some(c.name.name.to_string()),
1395                            ty,
1396                            init,
1397                        },
1398                        span,
1399                    );
1400
1401                    ctx.globals
1402                        .insert(c.name.name, LoweredGlobalDecl::Const(handle));
1403                    if !c.doc_comments.is_empty() {
1404                        ctx.module
1405                            .get_or_insert_default_doc_comments()
1406                            .constants
1407                            .insert(
1408                                handle,
1409                                c.doc_comments.iter().map(|s| s.to_string()).collect(),
1410                            );
1411                    }
1412                }
1413                ast::GlobalDeclKind::Override(ref o) => {
1414                    let explicit_ty =
1415                        o.ty.as_ref()
1416                            .map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const()))
1417                            .transpose()?;
1418
1419                    let mut ectx = ctx.as_override();
1420
1421                    let (ty, init) = self.type_and_init(
1422                        o.name,
1423                        o.init,
1424                        explicit_ty,
1425                        AbstractRule::Concretize,
1426                        &mut ectx,
1427                    )?;
1428
1429                    let id =
1430                        o.id.map(|id| self.const_u32(id, &mut ctx.as_const()))
1431                            .transpose()?;
1432
1433                    let id = if let Some((id, id_span)) = id {
1434                        Some(
1435                            u16::try_from(id)
1436                                .map_err(|_| Error::PipelineConstantIDValue(id_span))?,
1437                        )
1438                    } else {
1439                        None
1440                    };
1441
1442                    let handle = ctx.module.overrides.append(
1443                        ir::Override {
1444                            name: Some(o.name.name.to_string()),
1445                            id,
1446                            ty,
1447                            init,
1448                        },
1449                        span,
1450                    );
1451
1452                    ctx.globals
1453                        .insert(o.name.name, LoweredGlobalDecl::Override(handle));
1454                }
1455                ast::GlobalDeclKind::Struct(ref s) => {
1456                    let handle = self.r#struct(s, span, &mut ctx)?;
1457                    ctx.globals
1458                        .insert(s.name.name, LoweredGlobalDecl::Type(handle));
1459                    if !s.doc_comments.is_empty() {
1460                        ctx.module
1461                            .get_or_insert_default_doc_comments()
1462                            .types
1463                            .insert(
1464                                handle,
1465                                s.doc_comments.iter().map(|s| s.to_string()).collect(),
1466                            );
1467                    }
1468                }
1469                ast::GlobalDeclKind::Type(ref alias) => {
1470                    let ty = self.resolve_named_ast_type(
1471                        &alias.ty,
1472                        alias.name.name.to_string(),
1473                        &mut ctx.as_const(),
1474                    )?;
1475                    ctx.globals
1476                        .insert(alias.name.name, LoweredGlobalDecl::Type(ty));
1477                }
1478                ast::GlobalDeclKind::ConstAssert(condition) => {
1479                    let condition = self.expression(condition, &mut ctx.as_const())?;
1480
1481                    let span = ctx.module.global_expressions.get_span(condition);
1482                    match ctx
1483                        .module
1484                        .to_ctx()
1485                        .get_const_val_from(condition, &ctx.module.global_expressions)
1486                    {
1487                        Ok(true) => Ok(()),
1488                        Ok(false) => Err(Error::ConstAssertFailed(span)),
1489                        Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {
1490                            unreachable!()
1491                        }
1492                        Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),
1493                    }?;
1494                }
1495            }
1496        }
1497
1498        // Constant evaluation may leave abstract-typed literals and
1499        // compositions in expression arenas, so we need to compact the module
1500        // to remove unused expressions and types.
1501        crate::compact::compact(&mut module, KeepUnused::Yes);
1502
1503        Ok(module)
1504    }
1505
1506    /// Obtain (inferred) type and initializer after automatic conversion
1507    fn type_and_init(
1508        &mut self,
1509        name: ast::Ident<'source>,
1510        init: Option<Handle<ast::Expression<'source>>>,
1511        explicit_ty: Option<Handle<ir::Type>>,
1512        abstract_rule: AbstractRule,
1513        ectx: &mut ExpressionContext<'source, '_, '_>,
1514    ) -> Result<'source, (Handle<ir::Type>, Option<Handle<ir::Expression>>)> {
1515        let ty;
1516        let initializer;
1517        match (init, explicit_ty) {
1518            (Some(init), Some(explicit_ty)) => {
1519                let init = self.expression_for_abstract(init, ectx)?;
1520                let ty_res = proc::TypeResolution::Handle(explicit_ty);
1521                let init = ectx
1522                    .try_automatic_conversions(init, &ty_res, name.span)
1523                    .map_err(|error| match *error {
1524                        // Both of these mean the same thing to the reader of a
1525                        // `var`/`let` declaration: the initializer's type isn't
1526                        // the declared one.
1527                        Error::AutoConversion(e) => Box::new(Error::InitializationTypeMismatch {
1528                            name: name.span,
1529                            expected: e.dest_type,
1530                            got: e.source_type,
1531                        }),
1532                        Error::TypeMismatch(e) => Box::new(Error::InitializationTypeMismatch {
1533                            name: name.span,
1534                            expected: e.dest_type,
1535                            got: e.source_type,
1536                        }),
1537                        _ => error,
1538                    })?;
1539
1540                ty = explicit_ty;
1541                initializer = Some(init);
1542            }
1543            (Some(init), None) => {
1544                let mut init = self.expression_for_abstract(init, ectx)?;
1545                if let AbstractRule::Concretize = abstract_rule {
1546                    init = ectx.concretize(init)?;
1547                }
1548                ty = ectx.register_type(init)?;
1549                initializer = Some(init);
1550            }
1551            (None, Some(explicit_ty)) => {
1552                ty = explicit_ty;
1553                initializer = None;
1554            }
1555            (None, None) => return Err(Box::new(Error::DeclMissingTypeAndInit(name.span))),
1556        }
1557        Ok((ty, initializer))
1558    }
1559
1560    fn function(
1561        &mut self,
1562        f: &ast::Function<'source>,
1563        span: Span,
1564        ctx: &mut GlobalContext<'source, '_, '_>,
1565    ) -> Result<'source, LoweredGlobalDecl> {
1566        let mut local_table = FastHashMap::default();
1567        let mut expressions = Arena::new();
1568        let mut named_expressions = FastIndexMap::default();
1569        let mut local_expression_kind_tracker = proc::ExpressionKindTracker::new();
1570
1571        let arguments = f
1572            .arguments
1573            .iter()
1574            .enumerate()
1575            .map(|(i, arg)| -> Result<'_, _> {
1576                let ty = self.resolve_ast_type(&arg.ty, &mut ctx.as_const())?;
1577                let expr =
1578                    expressions.append(ir::Expression::FunctionArgument(i as u32), arg.name.span);
1579                local_table.insert(arg.handle, Declared::Runtime(Typed::Plain(expr)));
1580                named_expressions.insert(expr, (arg.name.name.to_string(), arg.name.span));
1581                local_expression_kind_tracker.insert(expr, proc::ExpressionKind::Runtime);
1582
1583                Ok(ir::FunctionArgument {
1584                    name: Some(arg.name.name.to_string()),
1585                    ty,
1586                    binding: self.binding(&arg.binding, ty, ctx)?,
1587                })
1588            })
1589            .collect::<Result<Vec<_>>>()?;
1590
1591        let result = f
1592            .result
1593            .as_ref()
1594            .map(|res| -> Result<'_, _> {
1595                let ty = self.resolve_ast_type(&res.ty, &mut ctx.as_const())?;
1596                Ok(ir::FunctionResult {
1597                    ty,
1598                    binding: self.binding(&res.binding, ty, ctx)?,
1599                })
1600            })
1601            .transpose()?;
1602
1603        let mut function = ir::Function {
1604            name: Some(f.name.name.to_string()),
1605            arguments,
1606            result,
1607            local_variables: Arena::new(),
1608            expressions,
1609            named_expressions: crate::NamedExpressions::default(),
1610            body: ir::Block::default(),
1611            diagnostic_filter_leaf: f.diagnostic_filter_leaf,
1612        };
1613
1614        let mut typifier = Typifier::default();
1615        let mut stmt_ctx = StatementContext {
1616            enable_extensions: ctx.enable_extensions,
1617            local_table: &mut local_table,
1618            globals: ctx.globals,
1619            ast_expressions: ctx.ast_expressions,
1620            const_typifier: ctx.const_typifier,
1621            typifier: &mut typifier,
1622            layouter: ctx.layouter,
1623            function: &mut function,
1624            named_expressions: &mut named_expressions,
1625            module: ctx.module,
1626            local_expression_kind_tracker: &mut local_expression_kind_tracker,
1627            global_expression_kind_tracker: ctx.global_expression_kind_tracker,
1628        };
1629        let mut body = self.block(&f.body, false, &mut stmt_ctx)?;
1630        proc::ensure_block_returns(&mut body);
1631
1632        function.body = body;
1633        function.named_expressions = named_expressions
1634            .into_iter()
1635            .map(|(key, (name, _))| (key, name))
1636            .collect();
1637
1638        if let Some(ref entry) = f.entry_point {
1639            let (workgroup_size, workgroup_size_overrides) =
1640                if let Some(workgroup_size) = entry.workgroup_size {
1641                    // TODO: replace with try_map once stabilized
1642                    let mut workgroup_size_out = [1; 3];
1643                    let mut workgroup_size_overrides_out = [None; 3];
1644                    for (i, size) in workgroup_size.into_iter().enumerate() {
1645                        if let Some(size_expr) = size {
1646                            match self.const_u32(size_expr, &mut ctx.as_const()) {
1647                                Ok(value) => {
1648                                    workgroup_size_out[i] = value.0;
1649                                }
1650                                Err(err) => {
1651                                    if let Error::ConstantEvaluatorError(ref ty, _) = *err {
1652                                        match **ty {
1653                                            proc::ConstantEvaluatorError::OverrideExpr => {
1654                                                workgroup_size_overrides_out[i] =
1655                                                    Some(self.workgroup_size_override(
1656                                                        size_expr,
1657                                                        &mut ctx.as_override(),
1658                                                    )?);
1659                                            }
1660                                            _ => {
1661                                                return Err(err);
1662                                            }
1663                                        }
1664                                    } else {
1665                                        return Err(err);
1666                                    }
1667                                }
1668                            }
1669                        }
1670                    }
1671                    if workgroup_size_overrides_out.iter().all(|x| x.is_none()) {
1672                        (workgroup_size_out, None)
1673                    } else {
1674                        (workgroup_size_out, Some(workgroup_size_overrides_out))
1675                    }
1676                } else {
1677                    ([0; 3], None)
1678                };
1679
1680            let mesh_info = if let Some((var_name, var_span)) = entry.mesh_output_variable {
1681                let var = match ctx.globals.get(var_name) {
1682                    Some(&LoweredGlobalDecl::Var(handle)) => handle,
1683                    Some(_) => {
1684                        return Err(Box::new(Error::ExpectedGlobalVariable {
1685                            name_span: var_span,
1686                        }))
1687                    }
1688                    None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1689                };
1690
1691                let mut info = ctx.module.analyze_mesh_shader_info(var);
1692                if let Some(h) = info.1[0] {
1693                    info.0.max_vertices_override = Some(
1694                        ctx.module
1695                            .global_expressions
1696                            .append(crate::Expression::Override(h), Span::UNDEFINED),
1697                    );
1698                }
1699                if let Some(h) = info.1[1] {
1700                    info.0.max_primitives_override = Some(
1701                        ctx.module
1702                            .global_expressions
1703                            .append(crate::Expression::Override(h), Span::UNDEFINED),
1704                    );
1705                }
1706
1707                Some(info.0)
1708            } else {
1709                None
1710            };
1711
1712            let task_payload = if let Some((var_name, var_span)) = entry.task_payload {
1713                Some(match ctx.globals.get(var_name) {
1714                    Some(&LoweredGlobalDecl::Var(handle)) => handle,
1715                    Some(_) => {
1716                        return Err(Box::new(Error::ExpectedGlobalVariable {
1717                            name_span: var_span,
1718                        }))
1719                    }
1720                    None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1721                })
1722            } else {
1723                None
1724            };
1725
1726            let incoming_ray_payload =
1727                if let Some((var_name, var_span)) = entry.ray_incoming_payload {
1728                    Some(match ctx.globals.get(var_name) {
1729                        Some(&LoweredGlobalDecl::Var(handle)) => handle,
1730                        Some(_) => {
1731                            return Err(Box::new(Error::ExpectedGlobalVariable {
1732                                name_span: var_span,
1733                            }))
1734                        }
1735                        None => return Err(Box::new(Error::UnknownIdent(var_span, var_name))),
1736                    })
1737                } else {
1738                    None
1739                };
1740
1741            ctx.module.entry_points.push(ir::EntryPoint {
1742                name: f.name.name.to_string(),
1743                stage: entry.stage,
1744                early_depth_test: entry.early_depth_test,
1745                workgroup_size,
1746                workgroup_size_overrides,
1747                function,
1748                mesh_info,
1749                task_payload,
1750                incoming_ray_payload,
1751            });
1752            Ok(LoweredGlobalDecl::EntryPoint(
1753                ctx.module.entry_points.len() - 1,
1754            ))
1755        } else {
1756            let handle = ctx.module.functions.append(function, span);
1757            Ok(LoweredGlobalDecl::Function {
1758                handle,
1759                must_use: f.result.as_ref().is_some_and(|res| res.must_use),
1760            })
1761        }
1762    }
1763
1764    fn workgroup_size_override(
1765        &mut self,
1766        size_expr: Handle<ast::Expression<'source>>,
1767        ctx: &mut ExpressionContext<'source, '_, '_>,
1768    ) -> Result<'source, Handle<ir::Expression>> {
1769        let span = ctx.ast_expressions.get_span(size_expr);
1770        let expr = self.expression(size_expr, ctx)?;
1771        match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
1772            Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok(expr),
1773            _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
1774                span,
1775            ))),
1776        }
1777    }
1778
1779    fn block(
1780        &mut self,
1781        b: &ast::Block<'source>,
1782        is_inside_loop: bool,
1783        ctx: &mut StatementContext<'source, '_, '_>,
1784    ) -> Result<'source, ir::Block> {
1785        let mut block = ir::Block::default();
1786
1787        for stmt in b.stmts.iter() {
1788            self.statement(stmt, &mut block, is_inside_loop, ctx)?;
1789        }
1790
1791        Ok(block)
1792    }
1793
1794    fn statement(
1795        &mut self,
1796        stmt: &ast::Statement<'source>,
1797        block: &mut ir::Block,
1798        is_inside_loop: bool,
1799        ctx: &mut StatementContext<'source, '_, '_>,
1800    ) -> Result<'source, ()> {
1801        let out = match stmt.kind {
1802            ast::StatementKind::Block(ref block) => {
1803                let block = self.block(block, is_inside_loop, ctx)?;
1804                ir::Statement::Block(block)
1805            }
1806            ast::StatementKind::LocalDecl(ref decl) => match *decl {
1807                ast::LocalDecl::Let(ref l) => {
1808                    let mut emitter = proc::Emitter::default();
1809                    emitter.start(&ctx.function.expressions);
1810
1811                    let explicit_ty = l
1812                        .ty
1813                        .as_ref()
1814                        .map(|ty| self.resolve_ast_type(ty, &mut ctx.as_const(block, &mut emitter)))
1815                        .transpose()?;
1816
1817                    let mut ectx = ctx.as_expression(block, &mut emitter);
1818
1819                    let (ty, initializer) = self.type_and_init(
1820                        l.name,
1821                        Some(l.init),
1822                        explicit_ty,
1823                        AbstractRule::Concretize,
1824                        &mut ectx,
1825                    )?;
1826
1827                    // We have this special check here for `let` declarations because the
1828                    // validator doesn't check them (they are comingled with other things in
1829                    // `named_expressions`; see <https://github.com/gfx-rs/wgpu/issues/7393>).
1830                    // The check could go in `type_and_init`, but then we'd have to
1831                    // distinguish whether override-sized is allowed. The error ought to use
1832                    // the type's span, but `module.types.get_span(ty)` is `Span::UNDEFINED`
1833                    // (see <https://github.com/gfx-rs/wgpu/issues/7951>).
1834                    if ctx.module.types[ty]
1835                        .inner
1836                        .is_dynamically_sized(&ctx.module.types)
1837                    {
1838                        return Err(Box::new(Error::TypeNotConstructible(l.name.span)));
1839                    }
1840
1841                    // We passed `Some()` to `type_and_init`, so we
1842                    // will get a lowered initializer expression back.
1843                    let initializer =
1844                        initializer.expect("type_and_init did not return an initializer");
1845
1846                    // The WGSL spec says that any expression that refers to a
1847                    // `let`-bound variable is not a const expression. This
1848                    // affects when errors must be reported, so we can't even
1849                    // treat suitable `let` bindings as constant as an
1850                    // optimization.
1851                    ctx.local_expression_kind_tracker
1852                        .force_non_const(initializer);
1853
1854                    block.extend(emitter.finish(&ctx.function.expressions));
1855                    ctx.local_table
1856                        .insert(l.handle, Declared::Runtime(Typed::Plain(initializer)));
1857                    ctx.named_expressions
1858                        .insert(initializer, (l.name.name.to_string(), l.name.span));
1859
1860                    if matches!(
1861                        ctx.module.types[ty].inner,
1862                        crate::TypeInner::RayQuery { .. }
1863                    ) {
1864                        // If a `let` variable is a ray query, it must be invalid as a `let`
1865                        // must have an initializer (it is also pretty useless as all other
1866                        // operations are disallowed, or require write-able variables).
1867                        return Err(Box::new(Error::RayQueryWithInitializer(
1868                            ctx.function.expressions.get_span(initializer),
1869                        )));
1870                    }
1871
1872                    return Ok(());
1873                }
1874                ast::LocalDecl::Var(ref v) => {
1875                    let mut emitter = proc::Emitter::default();
1876                    emitter.start(&ctx.function.expressions);
1877
1878                    let explicit_ty =
1879                        v.ty.as_ref()
1880                            .map(|ast| {
1881                                self.resolve_ast_type(ast, &mut ctx.as_const(block, &mut emitter))
1882                            })
1883                            .transpose()?;
1884
1885                    let mut ectx = ctx.as_expression(block, &mut emitter);
1886                    let (ty, initializer) = self.type_and_init(
1887                        v.name,
1888                        v.init,
1889                        explicit_ty,
1890                        AbstractRule::Concretize,
1891                        &mut ectx,
1892                    )?;
1893
1894                    let (const_initializer, initializer) = {
1895                        match initializer {
1896                            Some(init) => {
1897                                // It's not correct to hoist the initializer up
1898                                // to the top of the function if:
1899                                // - the initialization is inside a loop, and should
1900                                //   take place on every iteration, or
1901                                // - the initialization is not a constant
1902                                //   expression, so its value depends on the
1903                                //   state at the point of initialization.
1904                                if is_inside_loop
1905                                    || !ctx.local_expression_kind_tracker.is_const_or_override(init)
1906                                {
1907                                    (None, Some(init))
1908                                } else {
1909                                    (Some(init), None)
1910                                }
1911                            }
1912                            None => (None, None),
1913                        }
1914                    };
1915
1916                    let var = ctx.function.local_variables.append(
1917                        ir::LocalVariable {
1918                            name: Some(v.name.name.to_string()),
1919                            ty,
1920                            init: const_initializer,
1921                        },
1922                        stmt.span,
1923                    );
1924
1925                    let handle = ctx
1926                        .as_expression(block, &mut emitter)
1927                        .interrupt_emitter(ir::Expression::LocalVariable(var), Span::UNDEFINED)?;
1928
1929                    block.extend(emitter.finish(&ctx.function.expressions));
1930                    ctx.local_table
1931                        .insert(v.handle, Declared::Runtime(Typed::Reference(handle)));
1932
1933                    match ctx.module.types[ty].inner {
1934                        crate::TypeInner::RayQuery { .. } => {
1935                            // Initializers are disallowed for ray queries as any store is disallowed.
1936                            // However, in loops ray queries need to be reset using a special piece of
1937                            // IR.
1938
1939                            // Because we have a special case for ray queries, and initializers are always
1940                            // disallowed for ray queries, we remove them here. This prevents having to
1941                            // special-case them and then just emitting invalid IR anyway and gives a
1942                            // clearer error message.
1943                            if let Some(expr) = initializer {
1944                                return Err(Box::new(Error::RayQueryWithInitializer(
1945                                    ctx.function.expressions.get_span(expr),
1946                                )));
1947                            }
1948
1949                            if is_inside_loop {
1950                                ir::Statement::RayQuery {
1951                                    query: handle,
1952                                    fun: ir::RayQueryFunction::Begin,
1953                                }
1954                            } else {
1955                                return Ok(());
1956                            }
1957                        }
1958                        _ => {
1959                            let initializer = if is_inside_loop {
1960                                match initializer {
1961                                    Some(initializer) => Some(initializer),
1962                                    None => Some(
1963                                        ctx.as_expression(block, &mut emitter).append_expression(
1964                                            ir::Expression::ZeroValue(ty),
1965                                            stmt.span,
1966                                        )?,
1967                                    ),
1968                                }
1969                            } else {
1970                                initializer
1971                            };
1972
1973                            match initializer {
1974                                Some(initializer) => ir::Statement::Store {
1975                                    pointer: handle,
1976                                    value: initializer,
1977                                },
1978                                None => return Ok(()),
1979                            }
1980                        }
1981                    }
1982                }
1983                ast::LocalDecl::Const(ref c) => {
1984                    let mut emitter = proc::Emitter::default();
1985                    emitter.start(&ctx.function.expressions);
1986
1987                    let ectx = &mut ctx.as_const(block, &mut emitter);
1988
1989                    let explicit_ty =
1990                        c.ty.as_ref()
1991                            .map(|ast| self.resolve_ast_type(ast, &mut ectx.as_const()))
1992                            .transpose()?;
1993
1994                    let (_ty, init) = self.type_and_init(
1995                        c.name,
1996                        Some(c.init),
1997                        explicit_ty,
1998                        AbstractRule::Allow,
1999                        &mut ectx.as_const(),
2000                    )?;
2001                    let init = init.expect("Local const must have init");
2002
2003                    block.extend(emitter.finish(&ctx.function.expressions));
2004                    ctx.local_table
2005                        .insert(c.handle, Declared::Const(Typed::Plain(init)));
2006                    return Ok(());
2007                }
2008            },
2009            ast::StatementKind::If {
2010                condition,
2011                ref accept,
2012                ref reject,
2013            } => {
2014                let mut emitter = proc::Emitter::default();
2015                emitter.start(&ctx.function.expressions);
2016
2017                let condition =
2018                    self.expression(condition, &mut ctx.as_expression(block, &mut emitter))?;
2019                block.extend(emitter.finish(&ctx.function.expressions));
2020
2021                let accept = self.block(accept, is_inside_loop, ctx)?;
2022                let reject = self.block(reject, is_inside_loop, ctx)?;
2023
2024                ir::Statement::If {
2025                    condition,
2026                    accept,
2027                    reject,
2028                }
2029            }
2030            ast::StatementKind::Switch {
2031                selector,
2032                ref cases,
2033            } => {
2034                let mut emitter = proc::Emitter::default();
2035                emitter.start(&ctx.function.expressions);
2036
2037                let mut ectx = ctx.as_expression(block, &mut emitter);
2038
2039                // Determine the scalar type of the selector and case expressions, find the
2040                // consensus type for automatic conversion, then convert them.
2041                let (mut exprs, spans) = core::iter::once(selector)
2042                    .chain(cases.iter().filter_map(|case| match case.value {
2043                        ast::SwitchValue::Expr(expr) => Some(expr),
2044                        ast::SwitchValue::Default => None,
2045                    }))
2046                    .enumerate()
2047                    .map(|(i, expr)| {
2048                        let span = ectx.ast_expressions.get_span(expr);
2049                        let expr = self.expression_for_abstract(expr, &mut ectx)?;
2050                        let ty = resolve_inner!(ectx, expr);
2051                        match *ty {
2052                            ir::TypeInner::Scalar(
2053                                ir::Scalar::I32 | ir::Scalar::U32 | ir::Scalar::ABSTRACT_INT,
2054                            ) => Ok((expr, span)),
2055                            _ => match i {
2056                                0 => Err(Box::new(Error::InvalidSwitchSelector { span })),
2057                                _ => Err(Box::new(Error::InvalidSwitchCase { span })),
2058                            },
2059                        }
2060                    })
2061                    .collect::<Result<(Vec<_>, Vec<_>)>>()?;
2062
2063                let mut consensus =
2064                    ectx.automatic_conversion_consensus(None, &exprs)
2065                        .map_err(|span_idx| Error::SwitchCaseTypeMismatch {
2066                            span: spans[span_idx],
2067                        })?;
2068                // Concretize to I32 if the selector and all cases were abstract
2069                if consensus == ir::Scalar::ABSTRACT_INT {
2070                    consensus = ir::Scalar::I32;
2071                }
2072                for expr in &mut exprs {
2073                    ectx.convert_to_leaf_scalar(expr, consensus)?;
2074                }
2075
2076                block.extend(emitter.finish(&ctx.function.expressions));
2077
2078                let mut exprs = exprs.into_iter();
2079                let selector = exprs
2080                    .next()
2081                    .expect("First element should be selector expression");
2082
2083                let cases = cases
2084                    .iter()
2085                    .map(|case| {
2086                        Ok(ir::SwitchCase {
2087                            value: match case.value {
2088                                ast::SwitchValue::Expr(expr) => {
2089                                    let span = ctx.ast_expressions.get_span(expr);
2090                                    let expr = exprs.next().expect(
2091                                        "Should yield expression for each SwitchValue::Expr case",
2092                                    );
2093                                    match ctx
2094                                        .module
2095                                        .to_ctx()
2096                                        .get_const_val_from(expr, &ctx.function.expressions)
2097                                    {
2098                                        Ok(ir::Literal::I32(value)) => ir::SwitchValue::I32(value),
2099                                        Ok(ir::Literal::U32(value)) => ir::SwitchValue::U32(value),
2100                                        _ => {
2101                                            return Err(Box::new(Error::InvalidSwitchCase {
2102                                                span,
2103                                            }));
2104                                        }
2105                                    }
2106                                }
2107                                ast::SwitchValue::Default => ir::SwitchValue::Default,
2108                            },
2109                            body: self.block(&case.body, is_inside_loop, ctx)?,
2110                            fall_through: case.fall_through,
2111                        })
2112                    })
2113                    .collect::<Result<_>>()?;
2114
2115                ir::Statement::Switch { selector, cases }
2116            }
2117            ast::StatementKind::Loop {
2118                ref body,
2119                ref continuing,
2120                break_if,
2121            } => {
2122                let body = self.block(body, true, ctx)?;
2123                let mut continuing = self.block(continuing, true, ctx)?;
2124
2125                let mut emitter = proc::Emitter::default();
2126                emitter.start(&ctx.function.expressions);
2127                let break_if = break_if
2128                    .map(|expr| {
2129                        self.expression(expr, &mut ctx.as_expression(&mut continuing, &mut emitter))
2130                    })
2131                    .transpose()?;
2132                continuing.extend(emitter.finish(&ctx.function.expressions));
2133
2134                ir::Statement::Loop {
2135                    body,
2136                    continuing,
2137                    break_if,
2138                }
2139            }
2140            ast::StatementKind::Break => ir::Statement::Break,
2141            ast::StatementKind::Continue => ir::Statement::Continue,
2142            ast::StatementKind::Return { value: ast_value } => {
2143                let mut emitter = proc::Emitter::default();
2144                emitter.start(&ctx.function.expressions);
2145
2146                let value;
2147                if let Some(ast_expr) = ast_value {
2148                    let value_span = ctx.ast_expressions.get_span(ast_expr);
2149                    let result_ty = ctx.function.result.as_ref().map(|r| r.ty);
2150                    let mut ectx = ctx.as_expression(block, &mut emitter);
2151                    let expr = self.expression_for_abstract(ast_expr, &mut ectx)?;
2152
2153                    if let Some(result_ty) = result_ty {
2154                        let mut ectx = ctx.as_expression(block, &mut emitter);
2155                        let resolution = proc::TypeResolution::Handle(result_ty);
2156                        let converted =
2157                            ectx.try_automatic_conversions(expr, &resolution, value_span)?;
2158                        value = Some(converted);
2159                    } else {
2160                        value = Some(expr);
2161                    }
2162                } else {
2163                    value = None;
2164                }
2165                block.extend(emitter.finish(&ctx.function.expressions));
2166
2167                ir::Statement::Return { value }
2168            }
2169            ast::StatementKind::Kill => ir::Statement::Kill,
2170            ast::StatementKind::Call(ref call_phrase) => {
2171                let mut emitter = proc::Emitter::default();
2172                emitter.start(&ctx.function.expressions);
2173
2174                let _ = self.call(
2175                    call_phrase,
2176                    stmt.span,
2177                    &mut ctx.as_expression(block, &mut emitter),
2178                    true,
2179                )?;
2180                block.extend(emitter.finish(&ctx.function.expressions));
2181                return Ok(());
2182            }
2183            ast::StatementKind::Assign {
2184                target: ast_target,
2185                op,
2186                value,
2187            } => {
2188                let mut emitter = proc::Emitter::default();
2189                emitter.start(&ctx.function.expressions);
2190                let target_span = ctx.ast_expressions.get_span(ast_target);
2191
2192                let mut ectx = ctx.as_expression(block, &mut emitter);
2193                let target = self.expression_for_reference(ast_target, &mut ectx)?;
2194                let target_handle = match target {
2195                    Typed::Reference(handle) => handle,
2196                    Typed::Plain(handle) => {
2197                        let ty = ctx.invalid_assignment_type(handle);
2198                        return Err(Box::new(Error::InvalidAssignment {
2199                            span: target_span,
2200                            ty,
2201                        }));
2202                    }
2203                };
2204
2205                // Usually the value needs to be converted to match the type of
2206                // the memory view you're assigning it to. The bit shift
2207                // operators are exceptions, in that the right operand is always
2208                // a `u32` or `vecN<u32>`.
2209                let target_scalar = match op {
2210                    Some(ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight) => {
2211                        Some(ir::Scalar::U32)
2212                    }
2213                    _ => resolve_inner!(ectx, target_handle)
2214                        .pointer_automatically_convertible_scalar(&ectx.module.types),
2215                };
2216
2217                // Need to emit the LHS _before_ the RHS so that it is evaluated first.
2218                let op_assign = if let Some(op) = op {
2219                    Some((op, ectx.apply_load_rule(target)?))
2220                } else {
2221                    None
2222                };
2223
2224                let value = self.expression_for_abstract(value, &mut ectx)?;
2225                let mut value = match target_scalar {
2226                    Some(target_scalar) => ectx.try_automatic_conversion_for_leaf_scalar(
2227                        value,
2228                        target_scalar,
2229                        target_span,
2230                    )?,
2231                    None => value,
2232                };
2233
2234                let value = match op_assign {
2235                    Some((op, mut left)) => {
2236                        ectx.binary_op_splat(op, &mut left, &mut value)?;
2237                        ectx.append_expression(
2238                            ir::Expression::Binary {
2239                                op,
2240                                left,
2241                                right: value,
2242                            },
2243                            stmt.span,
2244                        )?
2245                    }
2246                    None => value,
2247                };
2248                block.extend(emitter.finish(&ctx.function.expressions));
2249
2250                ir::Statement::Store {
2251                    pointer: target_handle,
2252                    value,
2253                }
2254            }
2255            ast::StatementKind::Increment(value) | ast::StatementKind::Decrement(value) => {
2256                let mut emitter = proc::Emitter::default();
2257                emitter.start(&ctx.function.expressions);
2258
2259                let op = match stmt.kind {
2260                    ast::StatementKind::Increment(_) => ir::BinaryOperator::Add,
2261                    ast::StatementKind::Decrement(_) => ir::BinaryOperator::Subtract,
2262                    _ => unreachable!(),
2263                };
2264
2265                let value_span = ctx.ast_expressions.get_span(value);
2266                let target = self
2267                    .expression_for_reference(value, &mut ctx.as_expression(block, &mut emitter))?;
2268                let target_handle = target.ref_or(Error::BadIncrDecrReferenceType(value_span))?;
2269
2270                let mut ectx = ctx.as_expression(block, &mut emitter);
2271                let scalar = match *resolve_inner!(ectx, target_handle) {
2272                    ir::TypeInner::ValuePointer {
2273                        size: None, scalar, ..
2274                    } => scalar,
2275                    ir::TypeInner::Pointer { base, .. } => match ectx.module.types[base].inner {
2276                        ir::TypeInner::Scalar(scalar) => scalar,
2277                        _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2278                    },
2279                    _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2280                };
2281                let literal = match scalar.kind {
2282                    ir::ScalarKind::Sint | ir::ScalarKind::Uint => ir::Literal::one(scalar)
2283                        .ok_or(Error::BadIncrDecrReferenceType(value_span))?,
2284                    _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))),
2285                };
2286
2287                let right =
2288                    ectx.interrupt_emitter(ir::Expression::Literal(literal), Span::UNDEFINED)?;
2289                let rctx = ectx.runtime_expression_ctx(stmt.span)?;
2290                let left = rctx.function.expressions.append(
2291                    ir::Expression::Load {
2292                        pointer: target_handle,
2293                    },
2294                    value_span,
2295                );
2296                let value = rctx
2297                    .function
2298                    .expressions
2299                    .append(ir::Expression::Binary { op, left, right }, stmt.span);
2300                rctx.local_expression_kind_tracker
2301                    .insert(left, proc::ExpressionKind::Runtime);
2302                rctx.local_expression_kind_tracker
2303                    .insert(value, proc::ExpressionKind::Runtime);
2304
2305                block.extend(emitter.finish(&ctx.function.expressions));
2306                ir::Statement::Store {
2307                    pointer: target_handle,
2308                    value,
2309                }
2310            }
2311            ast::StatementKind::ConstAssert(condition) => {
2312                let mut emitter = proc::Emitter::default();
2313                emitter.start(&ctx.function.expressions);
2314
2315                let condition =
2316                    self.expression(condition, &mut ctx.as_const(block, &mut emitter))?;
2317
2318                let span = ctx.function.expressions.get_span(condition);
2319                match ctx
2320                    .module
2321                    .to_ctx()
2322                    .get_const_val_from(condition, &ctx.function.expressions)
2323                {
2324                    Ok(true) => Ok(()),
2325                    Ok(false) => Err(Error::ConstAssertFailed(span)),
2326                    Err(proc::ConstValueError::NonConst | proc::ConstValueError::Negative) => {
2327                        unreachable!()
2328                    }
2329                    Err(proc::ConstValueError::InvalidType) => Err(Error::NotBool(span)),
2330                }?;
2331
2332                block.extend(emitter.finish(&ctx.function.expressions));
2333
2334                return Ok(());
2335            }
2336            ast::StatementKind::Phony(expr) => {
2337                // Remembered the RHS of the phony assignment as a named expression. This
2338                // is important (1) to preserve the RHS for validation, (2) to track any
2339                // referenced globals.
2340                let mut emitter = proc::Emitter::default();
2341                emitter.start(&ctx.function.expressions);
2342
2343                let value = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?;
2344                block.extend(emitter.finish(&ctx.function.expressions));
2345                ctx.named_expressions
2346                    .insert(value, ("phony".to_string(), stmt.span));
2347                return Ok(());
2348            }
2349        };
2350
2351        block.push(out, stmt.span);
2352
2353        Ok(())
2354    }
2355
2356    /// Lower `expr` and apply the Load Rule if possible.
2357    ///
2358    /// For the time being, this concretizes abstract values, to support
2359    /// consumers that haven't been adapted to consume them yet. Consumers
2360    /// prepared for abstract values can call [`expression_for_abstract`].
2361    ///
2362    /// [`expression_for_abstract`]: Lowerer::expression_for_abstract
2363    fn expression(
2364        &mut self,
2365        expr: Handle<ast::Expression<'source>>,
2366        ctx: &mut ExpressionContext<'source, '_, '_>,
2367    ) -> Result<'source, Handle<ir::Expression>> {
2368        let expr = self.expression_for_abstract(expr, ctx)?;
2369        ctx.concretize(expr)
2370    }
2371
2372    fn expression_for_abstract(
2373        &mut self,
2374        expr: Handle<ast::Expression<'source>>,
2375        ctx: &mut ExpressionContext<'source, '_, '_>,
2376    ) -> Result<'source, Handle<ir::Expression>> {
2377        let expr = self.expression_for_reference(expr, ctx)?;
2378        ctx.apply_load_rule(expr)
2379    }
2380
2381    fn expression_with_leaf_scalar(
2382        &mut self,
2383        expr: Handle<ast::Expression<'source>>,
2384        scalar: ir::Scalar,
2385        ctx: &mut ExpressionContext<'source, '_, '_>,
2386    ) -> Result<'source, Handle<ir::Expression>> {
2387        let unconverted = self.expression_for_abstract(expr, ctx)?;
2388        ctx.try_automatic_conversion_for_leaf_scalar(unconverted, scalar, Span::default())
2389    }
2390
2391    fn expression_for_reference(
2392        &mut self,
2393        expr: Handle<ast::Expression<'source>>,
2394        ctx: &mut ExpressionContext<'source, '_, '_>,
2395    ) -> Result<'source, Typed<Handle<ir::Expression>>> {
2396        let span = ctx.ast_expressions.get_span(expr);
2397        let expr = &ctx.ast_expressions[expr];
2398
2399        let expr: Typed<ir::Expression> = match *expr {
2400            ast::Expression::Literal(literal) => {
2401                let literal = match literal {
2402                    ast::Literal::Number(Number::F16(f)) => ir::Literal::F16(f),
2403                    ast::Literal::Number(Number::F32(f)) => ir::Literal::F32(f),
2404                    ast::Literal::Number(Number::I32(i)) => ir::Literal::I32(i),
2405                    ast::Literal::Number(Number::U32(u)) => ir::Literal::U32(u),
2406                    ast::Literal::Number(Number::I64(i)) => ir::Literal::I64(i),
2407                    ast::Literal::Number(Number::U64(u)) => ir::Literal::U64(u),
2408                    ast::Literal::Number(Number::F64(f)) => ir::Literal::F64(f),
2409                    ast::Literal::Number(Number::AbstractInt(i)) => ir::Literal::AbstractInt(i),
2410                    ast::Literal::Number(Number::AbstractFloat(f)) => ir::Literal::AbstractFloat(f),
2411                    ast::Literal::Bool(b) => ir::Literal::Bool(b),
2412                };
2413                let handle = ctx.interrupt_emitter(ir::Expression::Literal(literal), span)?;
2414                return Ok(Typed::Plain(handle));
2415            }
2416            ast::Expression::Ident(ast::TemplateElaboratedIdent {
2417                ref template_list, ..
2418            }) if !template_list.is_empty() => {
2419                return Err(Box::new(Error::UnexpectedTemplate(span)))
2420            }
2421            ast::Expression::Ident(ast::TemplateElaboratedIdent {
2422                ident: ast::IdentExpr::Local(local),
2423                ..
2424            }) => {
2425                return ctx.local(&local, span);
2426            }
2427            ast::Expression::Ident(ast::TemplateElaboratedIdent {
2428                ident: ast::IdentExpr::Unresolved(name),
2429                ..
2430            }) => {
2431                let global = ctx
2432                    .globals
2433                    .get(name)
2434                    .ok_or(Error::UnknownIdent(span, name))?;
2435                let expr = match *global {
2436                    LoweredGlobalDecl::Var(handle) => {
2437                        let expr = ir::Expression::GlobalVariable(handle);
2438                        let v = &ctx.module.global_variables[handle];
2439                        match v.space {
2440                            ir::AddressSpace::Handle => Typed::Plain(expr),
2441                            _ => Typed::Reference(expr),
2442                        }
2443                    }
2444                    LoweredGlobalDecl::Const(handle) => {
2445                        Typed::Plain(ir::Expression::Constant(handle))
2446                    }
2447                    LoweredGlobalDecl::Override(handle) => {
2448                        Typed::Plain(ir::Expression::Override(handle))
2449                    }
2450                    LoweredGlobalDecl::Function { .. }
2451                    | LoweredGlobalDecl::Type(_)
2452                    | LoweredGlobalDecl::EntryPoint(_) => {
2453                        return Err(Box::new(Error::Unexpected(span, ExpectedToken::Variable)));
2454                    }
2455                };
2456
2457                return expr.try_map(|handle| ctx.interrupt_emitter(handle, span));
2458            }
2459            ast::Expression::Unary { op, expr } => self.unary(op, expr, span, ctx)?,
2460            ast::Expression::AddrOf(expr) => {
2461                // The `&` operator simply converts a reference to a pointer. And since a
2462                // reference is required, the Load Rule is not applied.
2463                match self.expression_for_reference(expr, ctx)? {
2464                    Typed::Reference(handle) => {
2465                        let expr = &ctx.runtime_expression_ctx(span)?.function.expressions[handle];
2466                        if let &ir::Expression::Access { base, .. }
2467                        | &ir::Expression::AccessIndex { base, .. } = expr
2468                        {
2469                            if let Some(ty) = resolve_inner!(ctx, base).pointer_base_type() {
2470                                if matches!(
2471                                    *ty.inner_with(&ctx.module.types),
2472                                    ir::TypeInner::Vector { .. },
2473                                ) {
2474                                    return Err(Box::new(Error::InvalidAddrOfOperand(
2475                                        ctx.get_expression_span(handle),
2476                                    )));
2477                                }
2478                            }
2479                        }
2480                        // No code is generated. We just declare the reference a pointer now.
2481                        return Ok(Typed::Plain(handle));
2482                    }
2483                    Typed::Plain(_) => {
2484                        return Err(Box::new(Error::NotReference(
2485                            "the operand of the `&` operator",
2486                            span,
2487                        )));
2488                    }
2489                }
2490            }
2491            ast::Expression::Deref(expr) => {
2492                // The pointer we dereference must be loaded.
2493                let pointer = self.expression(expr, ctx)?;
2494
2495                if resolve_inner!(ctx, pointer).pointer_space().is_none() {
2496                    return Err(Box::new(Error::NotPointer(span)));
2497                }
2498
2499                // No code is generated. We just declare the pointer a reference now.
2500                return Ok(Typed::Reference(pointer));
2501            }
2502            ast::Expression::Binary { op, left, right } => {
2503                self.binary(op, left, right, span, ctx)?
2504            }
2505            ast::Expression::Call(ref call_phrase) => {
2506                let handle = self
2507                    .call(call_phrase, span, ctx, false)?
2508                    .ok_or(Error::FunctionReturnsVoid(span))?;
2509                return Ok(Typed::Plain(handle));
2510            }
2511            ast::Expression::Index { base, index } => {
2512                let mut lowered_base = self.expression_for_reference(base, ctx)?;
2513                let index = self.expression(index, ctx)?;
2514
2515                // <https://www.w3.org/TR/WGSL/#language_extension-pointer_composite_access>
2516                // Declare pointer as reference
2517                if let Typed::Plain(handle) = lowered_base {
2518                    if resolve_inner!(ctx, handle).pointer_space().is_some() {
2519                        lowered_base = Typed::Reference(handle);
2520                    }
2521                }
2522
2523                lowered_base.try_map(|base| match ctx.get_const_val(index).ok() {
2524                    Some(index) => Ok::<_, Box<Error>>(ir::Expression::AccessIndex { base, index }),
2525                    None => {
2526                        // When an abstract array value e is indexed by an expression
2527                        // that is not a const-expression, then the array is concretized
2528                        // before the index is applied.
2529                        // https://www.w3.org/TR/WGSL/#array-access-expr
2530                        // Also applies to vectors and matrices.
2531                        let base = ctx.concretize(base)?;
2532                        Ok(ir::Expression::Access { base, index })
2533                    }
2534                })?
2535            }
2536            ast::Expression::Member { base, ref field } => {
2537                let mut lowered_base = self.expression_for_reference(base, ctx)?;
2538
2539                // <https://www.w3.org/TR/WGSL/#language_extension-pointer_composite_access>
2540                // Declare pointer as reference
2541                if let Typed::Plain(handle) = lowered_base {
2542                    if resolve_inner!(ctx, handle).pointer_space().is_some() {
2543                        lowered_base = Typed::Reference(handle);
2544                    }
2545                }
2546
2547                let temp_ty;
2548                let composite_type: &ir::TypeInner = match lowered_base {
2549                    Typed::Reference(handle) => {
2550                        temp_ty = resolve_inner!(ctx, handle)
2551                            .pointer_base_type()
2552                            .expect("In Typed::Reference(handle), handle must be a Naga pointer");
2553                        temp_ty.inner_with(&ctx.module.types)
2554                    }
2555
2556                    Typed::Plain(handle) => {
2557                        resolve_inner!(ctx, handle)
2558                    }
2559                };
2560
2561                let access = match *composite_type {
2562                    ir::TypeInner::Struct { ref members, .. } => {
2563                        let index = members
2564                            .iter()
2565                            .position(|m| m.name.as_deref() == Some(field.name))
2566                            .ok_or(Error::BadAccessor(field.span))?
2567                            as u32;
2568
2569                        lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2570                    }
2571                    ir::TypeInner::Vector { size: vec_size, .. } => {
2572                        match Components::new(field.name, field.span)? {
2573                            Components::Swizzle { size, pattern } => {
2574                                for &component in pattern[..size as usize].iter() {
2575                                    if component as u8 >= vec_size as u8 {
2576                                        return Err(Box::new(Error::BadAccessor(field.span)));
2577                                    }
2578                                }
2579                                Typed::Plain(ir::Expression::Swizzle {
2580                                    size,
2581                                    vector: ctx.apply_load_rule(lowered_base)?,
2582                                    pattern,
2583                                })
2584                            }
2585                            Components::Single(index) => {
2586                                if index >= vec_size as u32 {
2587                                    return Err(Box::new(Error::BadAccessor(field.span)));
2588                                }
2589                                lowered_base.map(|base| ir::Expression::AccessIndex { base, index })
2590                            }
2591                        }
2592                    }
2593                    _ => return Err(Box::new(Error::BadAccessor(field.span))),
2594                };
2595
2596                access
2597            }
2598            ast::Expression::String(_) => {
2599                return Err(Box::new(Error::InvalidStringLiteral {
2600                    span,
2601                    description: "String literals are only supported in debugPrintf",
2602                }))
2603            }
2604        };
2605
2606        expr.try_map(|handle| ctx.append_expression(handle, span))
2607    }
2608
2609    /// Generate IR for the short-circuiting operators `&&` and `||`.
2610    ///
2611    /// `binary` has already lowered the LHS expression and resolved its type.
2612    fn logical(
2613        &mut self,
2614        op: crate::BinaryOperator,
2615        left: Handle<crate::Expression>,
2616        right: Handle<ast::Expression<'source>>,
2617        span: Span,
2618        ctx: &mut ExpressionContext<'source, '_, '_>,
2619    ) -> Result<'source, Typed<crate::Expression>> {
2620        debug_assert!(
2621            op == crate::BinaryOperator::LogicalAnd || op == crate::BinaryOperator::LogicalOr
2622        );
2623
2624        if ctx.is_runtime() {
2625            // To simulate short-circuiting behavior, we want to generate IR
2626            // like the following for `&&`. For `||`, the condition is `!_lhs`
2627            // and the else value is `true`.
2628            //
2629            // var _e0: bool;
2630            // if _lhs {
2631            //     _e0 = _rhs;
2632            // } else {
2633            //     _e0 = false;
2634            // }
2635
2636            let (condition, else_val) = if op == crate::BinaryOperator::LogicalAnd {
2637                let condition = left;
2638                let else_val = ctx.append_expression(
2639                    crate::Expression::Literal(crate::Literal::Bool(false)),
2640                    span,
2641                )?;
2642                (condition, else_val)
2643            } else {
2644                let condition = ctx.append_expression(
2645                    crate::Expression::Unary {
2646                        op: crate::UnaryOperator::LogicalNot,
2647                        expr: left,
2648                    },
2649                    span,
2650                )?;
2651                let else_val = ctx.append_expression(
2652                    crate::Expression::Literal(crate::Literal::Bool(true)),
2653                    span,
2654                )?;
2655                (condition, else_val)
2656            };
2657
2658            let bool_ty = ctx.ensure_type_exists(crate::TypeInner::Scalar(crate::Scalar::BOOL));
2659
2660            let rctx = ctx.runtime_expression_ctx(span)?;
2661            let result_var = rctx.function.local_variables.append(
2662                crate::LocalVariable {
2663                    name: None,
2664                    ty: bool_ty,
2665                    init: None,
2666                },
2667                span,
2668            );
2669            let pointer =
2670                ctx.append_expression(crate::Expression::LocalVariable(result_var), span)?;
2671
2672            let (right, mut accept) = ctx.with_nested_runtime_expression_ctx(span, |ctx| {
2673                let right = self.expression_for_abstract(right, ctx)?;
2674                ctx.grow_types(right)?;
2675                Ok(right)
2676            })?;
2677
2678            accept.push(
2679                crate::Statement::Store {
2680                    pointer,
2681                    value: right,
2682                },
2683                span,
2684            );
2685
2686            let mut reject = crate::Block::with_capacity(1);
2687            reject.push(
2688                crate::Statement::Store {
2689                    pointer,
2690                    value: else_val,
2691                },
2692                span,
2693            );
2694
2695            let rctx = ctx.runtime_expression_ctx(span)?;
2696            rctx.block.push(
2697                crate::Statement::If {
2698                    condition,
2699                    accept,
2700                    reject,
2701                },
2702                span,
2703            );
2704
2705            Ok(Typed::Reference(crate::Expression::LocalVariable(
2706                result_var,
2707            )))
2708        } else {
2709            let left_val: Option<bool> = ctx.get_const_val(left).ok();
2710
2711            if left_val.is_some_and(|left_val| {
2712                op == crate::BinaryOperator::LogicalAnd && !left_val
2713                    || op == crate::BinaryOperator::LogicalOr && left_val
2714            }) {
2715                // Short-circuit behavior: don't evaluate the RHS.
2716
2717                // TODO(https://github.com/gfx-rs/wgpu/issues/8440): We shouldn't ignore the
2718                // RHS completely, it should still be type-checked. Preserving it for type
2719                // checking is a bit tricky, because we're trying to produce an expression
2720                // for a const context, but the RHS is allowed to have things that aren't
2721                // const.
2722
2723                Ok(Typed::Plain(ctx.get(left).clone()))
2724            } else {
2725                // Evaluate the RHS and construct the entire binary expression as we
2726                // normally would. This case applies to well-formed constant logical
2727                // expressions that don't short-circuit (handled by the constant evaluator
2728                // shortly), to override expressions (handled when overrides are processed)
2729                // and to non-well-formed expressions (rejected by type checking).
2730                let right = self.expression_for_abstract(right, ctx)?;
2731                ctx.grow_types(right)?;
2732
2733                Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
2734            }
2735        }
2736    }
2737
2738    fn type_expression(
2739        &mut self,
2740        expr: Handle<ast::Expression<'source>>,
2741        ctx: &mut ExpressionContext<'source, '_, '_>,
2742    ) -> Result<'source, Handle<ir::Type>> {
2743        let span = ctx.ast_expressions.get_span(expr);
2744        let expr = &ctx.ast_expressions[expr];
2745
2746        let ident = match *expr {
2747            ast::Expression::Ident(ref ident) => ident,
2748            _ => return Err(Box::new(Error::UnexpectedExprForTypeExpression(span))),
2749        };
2750
2751        self.type_specifier(ident, ctx, None)
2752    }
2753
2754    fn type_specifier(
2755        &mut self,
2756        ident: &ast::TemplateElaboratedIdent<'source>,
2757        ctx: &mut ExpressionContext<'source, '_, '_>,
2758        alias_name: Option<String>,
2759    ) -> Result<'source, Handle<ir::Type>> {
2760        let &ast::TemplateElaboratedIdent {
2761            ref ident,
2762            ident_span,
2763            ref template_list,
2764            ..
2765        } = ident;
2766
2767        let ident = match *ident {
2768            ast::IdentExpr::Unresolved(ident) => ident,
2769            ast::IdentExpr::Local(_) => {
2770                // Since WGSL only supports module-scope type definitions and
2771                // aliases, a local identifier can't possibly refer to a type.
2772                return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));
2773            }
2774        };
2775
2776        let mut tl = TemplateListIter::new(ident_span, template_list);
2777
2778        if let Some(global) = ctx.globals.get(ident) {
2779            let &LoweredGlobalDecl::Type(handle) = global else {
2780                return Err(Box::new(Error::UnexpectedExprForTypeExpression(ident_span)));
2781            };
2782
2783            // Type generators can only be predeclared, so since `ident` refers
2784            // to a module-scope declaration, the template parameter list should
2785            // be empty.
2786            tl.finish(ctx)?;
2787            return Ok(handle);
2788        }
2789
2790        // If `ident` doesn't resolve to a module-scope declaration, then it
2791        // must resolve to a predeclared type or type generator.
2792        let ty = conv::map_predeclared_type(&ctx.enable_extensions, ident_span, ident)?
2793            .ok_or_else(|| Box::new(Error::UnknownIdent(ident_span, ident)))?;
2794        let ty = self.finalize_type(ctx, ty, &mut tl, alias_name)?;
2795
2796        tl.finish(ctx)?;
2797
2798        Ok(ty)
2799    }
2800
2801    /// Construct an [`ir::Type`] from a [`conv::PredeclaredType`] and a list of
2802    /// template parameters.
2803    ///
2804    /// If we're processing a type alias, then `alias_name` is the name we
2805    /// should use in the new `ir::Type`.
2806    ///
2807    /// For example, when parsing `vec3<f32>`, the caller would pass:
2808    ///
2809    /// - for `ty`, [`TypeGenerator::Vector`], and
2810    ///
2811    /// - for `tl`, an iterator producing a single [`Expression::Ident`] representing `f32`.
2812    ///
2813    /// From those arguments this function will return a handle for the
2814    /// [`ir::Type`] representing `vec3<f32>`.
2815    ///
2816    /// [`TypeGenerator::Vector`]: conv::TypeGenerator::Vector
2817    /// [`Expression::Ident`]: crate::front::wgsl::parse::ast::Expression::Ident
2818    fn finalize_type(
2819        &mut self,
2820        ctx: &mut ExpressionContext<'source, '_, '_>,
2821        ty: conv::PredeclaredType,
2822        tl: &mut TemplateListIter<'_, 'source>,
2823        alias_name: Option<String>,
2824    ) -> Result<'source, Handle<ir::Type>> {
2825        let ty = match ty {
2826            conv::PredeclaredType::TypeInner(ty_inner) => {
2827                if let ir::TypeInner::Image {
2828                    class: ir::ImageClass::External,
2829                    ..
2830                } = ty_inner
2831                {
2832                    // Other than the WGSL backend, every backend that supports
2833                    // external textures does so by lowering them to a set of
2834                    // ordinary textures and some parameters saying how to
2835                    // sample from them. We don't know which backend will
2836                    // consume the `Module` we're building, but in case it's not
2837                    // WGSL, populate `SpecialTypes::external_texture_params`
2838                    // and `SpecialTypes::external_texture_transfer_function`
2839                    // with the types the backend will use for the parameter
2840                    // buffer.
2841                    //
2842                    // Neither of these are the type we are lowering here:
2843                    // that's an ordinary `TypeInner::Image`. But the fact we
2844                    // are lowering a `texture_external` implies the backends
2845                    // may need these additional types too.
2846                    ctx.module.generate_external_texture_types();
2847                }
2848
2849                ctx.as_global().ensure_type_exists(alias_name, ty_inner)
2850            }
2851            conv::PredeclaredType::RayDesc => ctx.module.generate_ray_desc_type(),
2852            conv::PredeclaredType::RayIntersection => ctx.module.generate_ray_intersection_type(),
2853            conv::PredeclaredType::TypeGenerator(type_generator) => {
2854                let ty_inner = match type_generator {
2855                    conv::TypeGenerator::Vector { size } => {
2856                        let (scalar, _) = tl.scalar_ty(self, ctx)?;
2857                        ir::TypeInner::Vector { size, scalar }
2858                    }
2859                    conv::TypeGenerator::Matrix { columns, rows } => {
2860                        let (scalar, span) = tl.scalar_ty(self, ctx)?;
2861                        if scalar.kind != ir::ScalarKind::Float {
2862                            return Err(Box::new(Error::BadMatrixScalarKind(span, scalar)));
2863                        }
2864                        ir::TypeInner::Matrix {
2865                            columns,
2866                            rows,
2867                            scalar,
2868                        }
2869                    }
2870                    conv::TypeGenerator::Array => {
2871                        let base = tl.ty(self, ctx)?;
2872                        let size = tl.maybe_array_size(self, ctx)?;
2873
2874                        // Determine the size of the base type, if needed.
2875                        ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
2876                            let LayoutErrorInner::TooLarge = err.inner else {
2877                                unreachable!("unexpected layout error: {err:?}");
2878                            };
2879                            // Lots of type definitions don't get spans, so this error
2880                            // message may not be very useful.
2881                            Box::new(Error::TypeTooLarge {
2882                                span: ctx.module.types.get_span(err.ty),
2883                            })
2884                        })?;
2885                        let stride = ctx.layouter[base].to_stride();
2886
2887                        ir::TypeInner::Array { base, size, stride }
2888                    }
2889                    conv::TypeGenerator::Atomic => {
2890                        let (scalar, _) = tl.scalar_ty(self, ctx)?;
2891                        ir::TypeInner::Atomic(scalar)
2892                    }
2893                    conv::TypeGenerator::Pointer => {
2894                        let mut space = tl.address_space(ctx)?;
2895                        let base = tl.ty(self, ctx)?;
2896                        tl.maybe_access_mode(&mut space, ctx)?;
2897                        ir::TypeInner::Pointer { base, space }
2898                    }
2899                    conv::TypeGenerator::SampledTexture {
2900                        dim,
2901                        arrayed,
2902                        multi,
2903                    } => {
2904                        let (scalar, span) = tl.scalar_ty(self, ctx)?;
2905                        let ir::Scalar { kind, width } = scalar;
2906                        if width != 4 {
2907                            return Err(Box::new(Error::BadTextureSampleType { span, scalar }));
2908                        }
2909                        ir::TypeInner::Image {
2910                            dim,
2911                            arrayed,
2912                            class: ir::ImageClass::Sampled { kind, multi },
2913                        }
2914                    }
2915                    conv::TypeGenerator::StorageTexture { dim, arrayed } => {
2916                        let format = tl.storage_format(ctx)?;
2917                        let access = tl.access_mode(ctx)?;
2918                        ir::TypeInner::Image {
2919                            dim,
2920                            arrayed,
2921                            class: ir::ImageClass::Storage { format, access },
2922                        }
2923                    }
2924                    conv::TypeGenerator::BindingArray => {
2925                        let base = tl.ty(self, ctx)?;
2926                        let size = tl.maybe_array_size(self, ctx)?;
2927                        ir::TypeInner::BindingArray { base, size }
2928                    }
2929                    conv::TypeGenerator::AccelerationStructure => {
2930                        let vertex_return = tl.maybe_vertex_return(ctx)?;
2931                        ir::TypeInner::AccelerationStructure { vertex_return }
2932                    }
2933                    conv::TypeGenerator::RayQuery => {
2934                        let vertex_return = tl.maybe_vertex_return(ctx)?;
2935                        ir::TypeInner::RayQuery { vertex_return }
2936                    }
2937                    conv::TypeGenerator::CooperativeMatrix { columns, rows } => {
2938                        let (ty, span) = tl.ty_with_span(self, ctx)?;
2939                        let ir::TypeInner::Scalar(scalar) = ctx.module.types[ty].inner else {
2940                            return Err(Box::new(Error::UnsupportedCooperativeScalar(span)));
2941                        };
2942                        let role = tl.cooperative_role(ctx)?;
2943                        ir::TypeInner::CooperativeMatrix {
2944                            columns,
2945                            rows,
2946                            scalar,
2947                            role,
2948                        }
2949                    }
2950                };
2951                ctx.as_global().ensure_type_exists(alias_name, ty_inner)
2952            }
2953        };
2954        Ok(ty)
2955    }
2956
2957    fn unary(
2958        &mut self,
2959        op: ir::UnaryOperator,
2960        expr: Handle<ast::Expression<'source>>,
2961        span: Span,
2962        ctx: &mut ExpressionContext<'source, '_, '_>,
2963    ) -> Result<'source, Typed<ir::Expression>> {
2964        let make_error = |operand_type: String| Error::InvalidUnaryOperandType {
2965            span,
2966            op,
2967            operand_type,
2968        };
2969
2970        let expr = self.expression_for_abstract(expr, ctx)?;
2971        ctx.grow_types(expr)?;
2972        let expr_ty_resolution = resolve!(ctx, expr);
2973
2974        // All unary operators are only defined for scalars and vectors of scalars.
2975        let Some(kind) = expr_ty_resolution
2976            .inner_with(&ctx.module.types)
2977            .vector_size_and_scalar()
2978            .map(|(_, scalar)| scalar.kind)
2979        else {
2980            let operand_type = ctx.type_resolution_to_string(expr_ty_resolution);
2981            return Err(Box::new(make_error(operand_type)));
2982        };
2983        // validate preconditions
2984        match (op, kind) {
2985            // `T` is `bool` or `vecN<bool>`. These types have no automatic conversions.
2986            (ir::UnaryOperator::LogicalNot, ir::ScalarKind::Bool) => {}
2987
2988            // `T` is `AbstractInt`, `AbstractFloat`, `i32`, `f32`, `f16`,
2989            // `vecN<AbstractInt>`, `vecN<AbstractFloat>`, `vecN<i32>`, `vecN<f32>`, or `vecN<f16>`.
2990            (
2991                ir::UnaryOperator::Negate,
2992                ir::ScalarKind::AbstractInt
2993                | ir::ScalarKind::AbstractFloat
2994                | ir::ScalarKind::Sint
2995                | ir::ScalarKind::Float,
2996            ) => {}
2997
2998            // `S` is `AbstractInt`, `i32`, or `u32`.
2999            // `T` is `S` or `vecN<S>`.
3000            (
3001                ir::UnaryOperator::BitwiseNot,
3002                ir::ScalarKind::Sint | ir::ScalarKind::Uint | ir::ScalarKind::AbstractInt,
3003            ) => {}
3004
3005            _ => {
3006                let operand_type = ctx.type_resolution_to_string(expr_ty_resolution);
3007                return Err(Box::new(make_error(operand_type)));
3008            }
3009        }
3010
3011        Ok(Typed::Plain(ir::Expression::Unary { op, expr }))
3012    }
3013
3014    fn binary(
3015        &mut self,
3016        op: ir::BinaryOperator,
3017        left: Handle<ast::Expression<'source>>,
3018        right: Handle<ast::Expression<'source>>,
3019        span: Span,
3020        ctx: &mut ExpressionContext<'source, '_, '_>,
3021    ) -> Result<'source, Typed<ir::Expression>> {
3022        if op == ir::BinaryOperator::LogicalAnd || op == ir::BinaryOperator::LogicalOr {
3023            let left = self.expression_for_abstract(left, ctx)?;
3024            ctx.grow_types(left)?;
3025
3026            if !matches!(
3027                resolve_inner!(ctx, left),
3028                &ir::TypeInner::Scalar(ir::Scalar::BOOL)
3029            ) {
3030                // Pass it through as-is, will fail validation
3031                let right = self.expression_for_abstract(right, ctx)?;
3032                ctx.grow_types(right)?;
3033                Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
3034            } else {
3035                self.logical(op, left, right, span, ctx)
3036            }
3037        } else {
3038            // Load both operands.
3039            let mut left = self.expression_for_abstract(left, ctx)?;
3040            let mut right = self.expression_for_abstract(right, ctx)?;
3041
3042            // Convert `scalar op vector` to `vector op vector` by introducing
3043            // `Splat` expressions.
3044            ctx.binary_op_splat(op, &mut left, &mut right)?;
3045
3046            // Apply automatic conversions.
3047            match op {
3048                ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight => {
3049                    // Shift operators require the right operand to be `u32` or
3050                    // `vecN<u32>`. We can let the validator sort out vector length
3051                    // issues, but the right operand must be, or convert to, a u32 leaf
3052                    // scalar.
3053                    right =
3054                        ctx.try_automatic_conversion_for_leaf_scalar(right, ir::Scalar::U32, span)?;
3055
3056                    // Additionally, we must concretize the left operand if the right operand
3057                    // is not a const-expression.
3058                    // See https://www.w3.org/TR/WGSL/#overload-resolution-section.
3059                    //
3060                    // 2. Eliminate any candidate where one of its subexpressions resolves to
3061                    // an abstract type after feasible automatic conversions, but another of
3062                    // the candidate’s subexpressions is not a const-expression.
3063                    //
3064                    // We only have to explicitly do so for shifts as their operands may be
3065                    // of different types - for other binary ops this is achieved by finding
3066                    // the conversion consensus for both operands.
3067                    if !ctx.is_const(right) {
3068                        left = ctx.concretize(left)?;
3069                    }
3070                }
3071
3072                // All other operators follow the same pattern: reconcile the
3073                // scalar leaf types. If there's no reconciliation possible,
3074                // leave the expressions as they are: validation will report the
3075                // problem.
3076                _ => {
3077                    ctx.grow_types(left)?;
3078                    ctx.grow_types(right)?;
3079                    if let Ok(consensus_scalar) =
3080                        ctx.automatic_conversion_consensus(None, [left, right].iter())
3081                    {
3082                        ctx.convert_to_leaf_scalar(&mut left, consensus_scalar)?;
3083                        ctx.convert_to_leaf_scalar(&mut right, consensus_scalar)?;
3084                    }
3085                }
3086            }
3087
3088            Ok(Typed::Plain(ir::Expression::Binary { op, left, right }))
3089        }
3090    }
3091
3092    /// Generate Naga IR for a call to a WGSL builtin function.
3093    #[allow(clippy::too_many_arguments)]
3094    fn call_builtin<'phrase>(
3095        &mut self,
3096        function_name: &'source str,
3097        function_span: Span,
3098        arguments: &[Handle<ast::Expression<'source>>],
3099        template_params: &mut TemplateListIter<'phrase, 'source>,
3100        call_span: Span,
3101        ctx: &mut ExpressionContext<'source, '_, '_>,
3102        is_statement: bool,
3103    ) -> Result<'source, Option<(Handle<ir::Expression>, MustUse)>> {
3104        let (expr, must_use) = if let Some(fun) = conv::map_relational_fun(function_name) {
3105            let mut args = ctx.prepare_args(arguments, 1, function_span);
3106            let argument = self.expression(args.next()?, ctx)?;
3107            args.finish()?;
3108
3109            // Check for no-op all(bool) and any(bool):
3110            let argument_unmodified = matches!(
3111                fun,
3112                ir::RelationalFunction::All | ir::RelationalFunction::Any
3113            ) && {
3114                matches!(
3115                    resolve_inner!(ctx, argument),
3116                    &ir::TypeInner::Scalar(ir::Scalar {
3117                        kind: ir::ScalarKind::Bool,
3118                        ..
3119                    })
3120                )
3121            };
3122
3123            if argument_unmodified {
3124                return Ok(Some((argument, MustUse::Yes)));
3125            } else {
3126                (ir::Expression::Relational { fun, argument }, MustUse::Yes)
3127            }
3128        } else if let Some((axis, ctrl)) = conv::map_derivative(function_name) {
3129            let mut args = ctx.prepare_args(arguments, 1, function_span);
3130            let expr = self.expression(args.next()?, ctx)?;
3131            args.finish()?;
3132
3133            (
3134                ir::Expression::Derivative { axis, ctrl, expr },
3135                MustUse::Yes,
3136            )
3137        } else if let Some(fun) = conv::map_standard_fun(function_name) {
3138            (
3139                self.math_function_helper(function_span, fun, arguments, ctx)?,
3140                MustUse::Yes,
3141            )
3142        } else if let Some(fun) = Texture::map(function_name) {
3143            (
3144                self.texture_sample_helper(fun, arguments, function_span, ctx)?,
3145                MustUse::Yes,
3146            )
3147        } else if let Some((op, cop)) = conv::map_subgroup_operation(function_name) {
3148            return Ok(Some((
3149                self.subgroup_operation_helper(function_span, op, cop, arguments, ctx)?,
3150                MustUse::Yes,
3151            )));
3152        } else if let Some(mode) = SubgroupGather::map(function_name) {
3153            return Ok(Some((
3154                self.subgroup_gather_helper(function_span, mode, arguments, ctx)?,
3155                MustUse::Yes,
3156            )));
3157        } else if let Some(fun) = ir::AtomicFunction::map(function_name) {
3158            return Ok(self
3159                .atomic_helper(function_span, fun, arguments, is_statement, ctx)?
3160                .map(|result| (result, MustUse::No)));
3161        } else {
3162            match function_name {
3163                "bitcast" => {
3164                    let ty = template_params.ty(self, ctx)?;
3165
3166                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3167                    let expr = self.expression(args.next()?, ctx)?;
3168                    args.finish()?;
3169
3170                    let element_scalar = match ctx.module.types[ty].inner {
3171                        ir::TypeInner::Scalar(scalar) => scalar,
3172                        ir::TypeInner::Vector { scalar, .. } => scalar,
3173                        _ => {
3174                            let ty_resolution = resolve!(ctx, expr);
3175                            return Err(Box::new(Error::BadTypeCast {
3176                                from_type: ctx.type_resolution_to_string(ty_resolution),
3177                                span: function_span,
3178                                to_type: ctx.type_to_string(ty),
3179                            }));
3180                        }
3181                    };
3182
3183                    (
3184                        ir::Expression::As {
3185                            expr,
3186                            kind: element_scalar.kind,
3187                            convert: None,
3188                        },
3189                        MustUse::Yes,
3190                    )
3191                }
3192                "coopLoad" | "coopLoadT" => {
3193                    let row_major = function_name.ends_with("T");
3194                    let (matrix_ty, matrix_span) = template_params.ty_with_span(self, ctx)?;
3195
3196                    let mut args = ctx.prepare_args(arguments, 1, call_span);
3197                    let pointer = self.expression(args.next()?, ctx)?;
3198                    let (columns, rows, role) = match ctx.module.types[matrix_ty].inner {
3199                        ir::TypeInner::CooperativeMatrix {
3200                            columns,
3201                            rows,
3202                            role,
3203                            ..
3204                        } => (columns, rows, role),
3205                        _ => return Err(Box::new(Error::InvalidCooperativeLoadType(matrix_span))),
3206                    };
3207                    let stride = if args.total_args > 1 {
3208                        self.expression(args.next()?, ctx)?
3209                    } else {
3210                        // Infer the stride from the matrix type
3211                        let stride = if row_major {
3212                            columns as u32
3213                        } else {
3214                            rows as u32
3215                        };
3216                        ctx.append_expression(
3217                            ir::Expression::Literal(ir::Literal::U32(stride)),
3218                            Span::UNDEFINED,
3219                        )?
3220                    };
3221                    args.finish()?;
3222
3223                    (
3224                        crate::Expression::CooperativeLoad {
3225                            columns,
3226                            rows,
3227                            role,
3228                            data: crate::CooperativeData {
3229                                pointer,
3230                                stride,
3231                                row_major,
3232                            },
3233                        },
3234                        MustUse::Yes,
3235                    )
3236                }
3237                "select" => {
3238                    let mut args = ctx.prepare_args(arguments, 3, function_span);
3239
3240                    let reject_orig = args.next()?;
3241                    let accept_orig = args.next()?;
3242                    let mut values = [
3243                        self.expression_for_abstract(reject_orig, ctx)?,
3244                        self.expression_for_abstract(accept_orig, ctx)?,
3245                    ];
3246                    let condition = self.expression(args.next()?, ctx)?;
3247
3248                    args.finish()?;
3249
3250                    let diagnostic_details =
3251                        |ctx: &ExpressionContext<'_, '_, '_>,
3252                         ty_res: &proc::TypeResolution,
3253                         orig_expr| {
3254                            (
3255                                ctx.ast_expressions.get_span(orig_expr),
3256                                format!("`{}`", ctx.as_diagnostic_display(ty_res)),
3257                            )
3258                        };
3259                    for (&value, orig_value) in values.iter().zip([reject_orig, accept_orig]) {
3260                        let value_ty_res = resolve!(ctx, value);
3261                        if value_ty_res
3262                            .inner_with(&ctx.module.types)
3263                            .vector_size_and_scalar()
3264                            .is_none()
3265                        {
3266                            let (arg_span, arg_type) =
3267                                diagnostic_details(ctx, value_ty_res, orig_value);
3268                            return Err(Box::new(Error::SelectUnexpectedArgumentType {
3269                                arg_span,
3270                                arg_type,
3271                            }));
3272                        }
3273                    }
3274                    let mut consensus_scalar = ctx
3275                        .automatic_conversion_consensus(None, &values)
3276                        .map_err(|_idx| {
3277                            let [reject, accept] = values;
3278                            let [(reject_span, reject_type), (accept_span, accept_type)] =
3279                                [(reject_orig, reject), (accept_orig, accept)].map(
3280                                    |(orig_expr, expr)| {
3281                                        let ty_res = &ctx.typifier()[expr];
3282                                        diagnostic_details(ctx, ty_res, orig_expr)
3283                                    },
3284                                );
3285                            Error::SelectRejectAndAcceptHaveNoCommonType {
3286                                reject_span,
3287                                reject_type,
3288                                accept_span,
3289                                accept_type,
3290                            }
3291                        })?;
3292                    if !ctx.is_const(condition) {
3293                        consensus_scalar = consensus_scalar.concretize();
3294                    }
3295
3296                    ctx.convert_slice_to_common_leaf_scalar(&mut values, consensus_scalar)?;
3297
3298                    let [reject, accept] = values;
3299
3300                    (
3301                        ir::Expression::Select {
3302                            reject,
3303                            accept,
3304                            condition,
3305                        },
3306                        MustUse::Yes,
3307                    )
3308                }
3309                "arrayLength" => {
3310                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3311                    let expr = self.expression(args.next()?, ctx)?;
3312                    args.finish()?;
3313
3314                    (ir::Expression::ArrayLength(expr), MustUse::Yes)
3315                }
3316                "atomicLoad" => {
3317                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3318                    let (pointer, _scalar) = self.atomic_pointer(args.next()?, ctx)?;
3319                    args.finish()?;
3320
3321                    (ir::Expression::Load { pointer }, MustUse::No)
3322                }
3323                "atomicStore" => {
3324                    let mut args = ctx.prepare_args(arguments, 2, function_span);
3325                    let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
3326                    let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3327                    args.finish()?;
3328
3329                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3330                    rctx.block
3331                        .extend(rctx.emitter.finish(&rctx.function.expressions));
3332                    rctx.emitter.start(&rctx.function.expressions);
3333                    rctx.block
3334                        .push(ir::Statement::Store { pointer, value }, function_span);
3335                    return Ok(None);
3336                }
3337                "atomicCompareExchangeWeak" => {
3338                    let mut args = ctx.prepare_args(arguments, 3, function_span);
3339
3340                    let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
3341
3342                    let compare = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3343
3344                    let value = args.next()?;
3345                    let value_span = ctx.ast_expressions.get_span(value);
3346                    let value = self.expression_with_leaf_scalar(value, scalar, ctx)?;
3347
3348                    args.finish()?;
3349
3350                    let expression = match *resolve_inner!(ctx, value) {
3351                        ir::TypeInner::Scalar(scalar) => ir::Expression::AtomicResult {
3352                            ty: ctx.module.generate_predeclared_type(
3353                                ir::PredeclaredType::AtomicCompareExchangeWeakResult(scalar),
3354                            ),
3355                            comparison: true,
3356                        },
3357                        _ => return Err(Box::new(Error::InvalidAtomicOperandType(value_span))),
3358                    };
3359
3360                    let result = ctx.interrupt_emitter(expression, function_span)?;
3361                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3362                    rctx.block.push(
3363                        ir::Statement::Atomic {
3364                            pointer,
3365                            fun: ir::AtomicFunction::Exchange {
3366                                compare: Some(compare),
3367                            },
3368                            value,
3369                            result: Some(result),
3370                        },
3371                        function_span,
3372                    );
3373                    return Ok(Some((result, MustUse::No)));
3374                }
3375                "textureAtomicMin" | "textureAtomicMax" | "textureAtomicAdd"
3376                | "textureAtomicAnd" | "textureAtomicOr" | "textureAtomicXor" => {
3377                    let mut args = ctx.prepare_args(arguments, 3, function_span);
3378
3379                    let image = args.next()?;
3380                    let image_span = ctx.ast_expressions.get_span(image);
3381                    let image = self.expression(image, ctx)?;
3382
3383                    let coordinate = self.expression(args.next()?, ctx)?;
3384
3385                    let (_, arrayed) = ctx.image_data(image, image_span)?;
3386                    let array_index = arrayed
3387                        .then(|| {
3388                            args.min_args += 1;
3389                            self.expression(args.next()?, ctx)
3390                        })
3391                        .transpose()?;
3392
3393                    let value = self.expression(args.next()?, ctx)?;
3394
3395                    args.finish()?;
3396
3397                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3398                    rctx.block
3399                        .extend(rctx.emitter.finish(&rctx.function.expressions));
3400                    rctx.emitter.start(&rctx.function.expressions);
3401                    let stmt = ir::Statement::ImageAtomic {
3402                        image,
3403                        coordinate,
3404                        array_index,
3405                        fun: match function_name {
3406                            "textureAtomicMin" => ir::AtomicFunction::Min,
3407                            "textureAtomicMax" => ir::AtomicFunction::Max,
3408                            "textureAtomicAdd" => ir::AtomicFunction::Add,
3409                            "textureAtomicAnd" => ir::AtomicFunction::And,
3410                            "textureAtomicOr" => ir::AtomicFunction::InclusiveOr,
3411                            "textureAtomicXor" => ir::AtomicFunction::ExclusiveOr,
3412                            _ => unreachable!(),
3413                        },
3414                        value,
3415                    };
3416                    rctx.block.push(stmt, function_span);
3417                    return Ok(None);
3418                }
3419                "storageBarrier" => {
3420                    ctx.prepare_args(arguments, 0, function_span).finish()?;
3421
3422                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3423                    rctx.block.push(
3424                        ir::Statement::ControlBarrier(ir::Barrier::STORAGE),
3425                        function_span,
3426                    );
3427                    return Ok(None);
3428                }
3429                "workgroupBarrier" => {
3430                    ctx.prepare_args(arguments, 0, function_span).finish()?;
3431
3432                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3433                    rctx.block.push(
3434                        ir::Statement::ControlBarrier(ir::Barrier::WORK_GROUP),
3435                        function_span,
3436                    );
3437                    return Ok(None);
3438                }
3439                "subgroupBarrier" => {
3440                    ctx.prepare_args(arguments, 0, function_span).finish()?;
3441
3442                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3443                    rctx.block.push(
3444                        ir::Statement::ControlBarrier(ir::Barrier::SUB_GROUP),
3445                        function_span,
3446                    );
3447                    return Ok(None);
3448                }
3449                "textureBarrier" => {
3450                    ctx.prepare_args(arguments, 0, function_span).finish()?;
3451
3452                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3453                    rctx.block.push(
3454                        ir::Statement::ControlBarrier(ir::Barrier::TEXTURE),
3455                        function_span,
3456                    );
3457                    return Ok(None);
3458                }
3459                "workgroupUniformLoad" => {
3460                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3461                    let expr = args.next()?;
3462                    args.finish()?;
3463
3464                    let pointer = self.expression(expr, ctx)?;
3465                    let result_ty = match *resolve_inner!(ctx, pointer) {
3466                        ir::TypeInner::Pointer {
3467                            base,
3468                            space: ir::AddressSpace::WorkGroup,
3469                        } => match ctx.module.types[base].inner {
3470                            // Match `Expression::Load` semantics:
3471                            // loading through a pointer to `atomic<T>` produces a `T`.
3472                            ir::TypeInner::Atomic(scalar) => ctx.module.types.insert(
3473                                ir::Type {
3474                                    name: None,
3475                                    inner: ir::TypeInner::Scalar(scalar),
3476                                },
3477                                function_span,
3478                            ),
3479                            _ => base,
3480                        },
3481                        ir::TypeInner::ValuePointer {
3482                            size,
3483                            scalar,
3484                            space: ir::AddressSpace::WorkGroup,
3485                        } => ctx.module.types.insert(
3486                            ir::Type {
3487                                name: None,
3488                                inner: match size {
3489                                    Some(size) => ir::TypeInner::Vector { size, scalar },
3490                                    None => ir::TypeInner::Scalar(scalar),
3491                                },
3492                            },
3493                            function_span,
3494                        ),
3495                        _ => {
3496                            let span = ctx.ast_expressions.get_span(expr);
3497                            return Err(Box::new(Error::InvalidWorkGroupUniformLoad(span)));
3498                        }
3499                    };
3500                    let result = ctx.interrupt_emitter(
3501                        ir::Expression::WorkGroupUniformLoadResult { ty: result_ty },
3502                        function_span,
3503                    )?;
3504                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3505                    rctx.block.push(
3506                        ir::Statement::WorkGroupUniformLoad { pointer, result },
3507                        function_span,
3508                    );
3509
3510                    return Ok(Some((result, MustUse::Yes)));
3511                }
3512                "textureStore" => {
3513                    let mut args = ctx.prepare_args(arguments, 3, function_span);
3514
3515                    let image = args.next()?;
3516                    let image_span = ctx.ast_expressions.get_span(image);
3517                    let image = self.expression(image, ctx)?;
3518
3519                    let coordinate = self.expression(args.next()?, ctx)?;
3520
3521                    let (class, arrayed) = ctx.image_data(image, image_span)?;
3522                    let array_index = arrayed
3523                        .then(|| {
3524                            args.min_args += 1;
3525                            self.expression(args.next()?, ctx)
3526                        })
3527                        .transpose()?;
3528                    let scalar = if let ir::ImageClass::Storage { format, .. } = class {
3529                        format.into()
3530                    } else {
3531                        return Err(Box::new(Error::NotStorageTexture(image_span)));
3532                    };
3533
3534                    let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
3535
3536                    args.finish()?;
3537
3538                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3539                    rctx.block
3540                        .extend(rctx.emitter.finish(&rctx.function.expressions));
3541                    rctx.emitter.start(&rctx.function.expressions);
3542                    let stmt = ir::Statement::ImageStore {
3543                        image,
3544                        coordinate,
3545                        array_index,
3546                        value,
3547                    };
3548                    rctx.block.push(stmt, function_span);
3549                    return Ok(None);
3550                }
3551                "textureLoad" => {
3552                    let mut args = ctx.prepare_args(arguments, 2, function_span);
3553
3554                    let image = args.next()?;
3555                    let image_span = ctx.ast_expressions.get_span(image);
3556                    let image = self.expression(image, ctx)?;
3557
3558                    let coordinate = self.expression(args.next()?, ctx)?;
3559
3560                    let (class, arrayed) = ctx.image_data(image, image_span)?;
3561                    let array_index = arrayed
3562                        .then(|| {
3563                            args.min_args += 1;
3564                            self.expression(args.next()?, ctx)
3565                        })
3566                        .transpose()?;
3567
3568                    let level = class
3569                        .is_mipmapped()
3570                        .then(|| {
3571                            args.min_args += 1;
3572                            self.expression(args.next()?, ctx)
3573                        })
3574                        .transpose()?;
3575
3576                    let sample = class
3577                        .is_multisampled()
3578                        .then(|| self.expression(args.next()?, ctx))
3579                        .transpose()?;
3580
3581                    args.finish()?;
3582
3583                    (
3584                        ir::Expression::ImageLoad {
3585                            image,
3586                            coordinate,
3587                            array_index,
3588                            level,
3589                            sample,
3590                        },
3591                        MustUse::Yes,
3592                    )
3593                }
3594                "textureDimensions" => {
3595                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3596                    let image = self.expression(args.next()?, ctx)?;
3597                    let level = args
3598                        .next()
3599                        .map(|arg| self.expression(arg, ctx))
3600                        .ok()
3601                        .transpose()?;
3602                    args.finish()?;
3603
3604                    (
3605                        ir::Expression::ImageQuery {
3606                            image,
3607                            query: ir::ImageQuery::Size { level },
3608                        },
3609                        MustUse::Yes,
3610                    )
3611                }
3612                "textureNumLevels" => {
3613                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3614                    let image = self.expression(args.next()?, ctx)?;
3615                    args.finish()?;
3616
3617                    (
3618                        ir::Expression::ImageQuery {
3619                            image,
3620                            query: ir::ImageQuery::NumLevels,
3621                        },
3622                        MustUse::Yes,
3623                    )
3624                }
3625                "textureNumLayers" => {
3626                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3627                    let image = self.expression(args.next()?, ctx)?;
3628                    args.finish()?;
3629
3630                    (
3631                        ir::Expression::ImageQuery {
3632                            image,
3633                            query: ir::ImageQuery::NumLayers,
3634                        },
3635                        MustUse::Yes,
3636                    )
3637                }
3638                "textureNumSamples" => {
3639                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3640                    let image = self.expression(args.next()?, ctx)?;
3641                    args.finish()?;
3642
3643                    (
3644                        ir::Expression::ImageQuery {
3645                            image,
3646                            query: ir::ImageQuery::NumSamples,
3647                        },
3648                        MustUse::Yes,
3649                    )
3650                }
3651                "rayQueryInitialize" => {
3652                    let mut args = ctx.prepare_args(arguments, 3, function_span);
3653                    let query = self.ray_query_pointer(args.next()?, ctx)?;
3654                    let acceleration_structure = self.expression(args.next()?, ctx)?;
3655                    let descriptor = self.expression(args.next()?, ctx)?;
3656                    args.finish()?;
3657
3658                    let _ = ctx.module.generate_ray_desc_type();
3659                    let fun = ir::RayQueryFunction::Initialize {
3660                        acceleration_structure,
3661                        descriptor,
3662                    };
3663
3664                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3665                    rctx.block
3666                        .extend(rctx.emitter.finish(&rctx.function.expressions));
3667                    rctx.emitter.start(&rctx.function.expressions);
3668                    rctx.block
3669                        .push(ir::Statement::RayQuery { query, fun }, function_span);
3670                    return Ok(None);
3671                }
3672                "getCommittedHitVertexPositions" => {
3673                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3674                    let query = self.ray_query_pointer(args.next()?, ctx)?;
3675                    args.finish()?;
3676
3677                    let _ = ctx.module.generate_vertex_return_type();
3678
3679                    (
3680                        ir::Expression::RayQueryVertexPositions {
3681                            query,
3682                            committed: true,
3683                        },
3684                        MustUse::No,
3685                    )
3686                }
3687                "getCandidateHitVertexPositions" => {
3688                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3689                    let query = self.ray_query_pointer(args.next()?, ctx)?;
3690                    args.finish()?;
3691
3692                    let _ = ctx.module.generate_vertex_return_type();
3693
3694                    (
3695                        ir::Expression::RayQueryVertexPositions {
3696                            query,
3697                            committed: false,
3698                        },
3699                        MustUse::No,
3700                    )
3701                }
3702                "rayQueryProceed" => {
3703                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3704                    let query = self.ray_query_pointer(args.next()?, ctx)?;
3705                    args.finish()?;
3706
3707                    let result = ctx
3708                        .interrupt_emitter(ir::Expression::RayQueryProceedResult, function_span)?;
3709                    let fun = ir::RayQueryFunction::Proceed { result };
3710                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3711                    rctx.block
3712                        .push(ir::Statement::RayQuery { query, fun }, function_span);
3713                    return Ok(Some((result, MustUse::No)));
3714                }
3715                "rayQueryGenerateIntersection" => {
3716                    let mut args = ctx.prepare_args(arguments, 2, function_span);
3717                    let query = self.ray_query_pointer(args.next()?, ctx)?;
3718                    let hit_t = self.expression(args.next()?, ctx)?;
3719                    args.finish()?;
3720
3721                    let fun = ir::RayQueryFunction::GenerateIntersection { hit_t };
3722                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3723                    rctx.block
3724                        .push(ir::Statement::RayQuery { query, fun }, function_span);
3725                    return Ok(None);
3726                }
3727                "rayQueryConfirmIntersection" => {
3728                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3729                    let query = self.ray_query_pointer(args.next()?, ctx)?;
3730                    args.finish()?;
3731
3732                    let fun = ir::RayQueryFunction::ConfirmIntersection;
3733                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3734                    rctx.block
3735                        .push(ir::Statement::RayQuery { query, fun }, function_span);
3736                    return Ok(None);
3737                }
3738                "rayQueryTerminate" => {
3739                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3740                    let query = self.ray_query_pointer(args.next()?, ctx)?;
3741                    args.finish()?;
3742
3743                    let fun = ir::RayQueryFunction::Terminate;
3744                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3745                    rctx.block
3746                        .push(ir::Statement::RayQuery { query, fun }, function_span);
3747                    return Ok(None);
3748                }
3749                "rayQueryGetCommittedIntersection" => {
3750                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3751                    let query = self.ray_query_pointer(args.next()?, ctx)?;
3752                    args.finish()?;
3753
3754                    let _ = ctx.module.generate_ray_intersection_type();
3755                    (
3756                        ir::Expression::RayQueryGetIntersection {
3757                            query,
3758                            committed: true,
3759                        },
3760                        MustUse::No,
3761                    )
3762                }
3763                "rayQueryGetCandidateIntersection" => {
3764                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3765                    let query = self.ray_query_pointer(args.next()?, ctx)?;
3766                    args.finish()?;
3767
3768                    let _ = ctx.module.generate_ray_intersection_type();
3769                    (
3770                        ir::Expression::RayQueryGetIntersection {
3771                            query,
3772                            committed: false,
3773                        },
3774                        MustUse::No,
3775                    )
3776                }
3777                "subgroupBallot" => {
3778                    let mut args = ctx.prepare_args(arguments, 0, function_span);
3779                    let predicate = if arguments.len() == 1 {
3780                        Some(self.expression(args.next()?, ctx)?)
3781                    } else {
3782                        None
3783                    };
3784                    args.finish()?;
3785
3786                    let result =
3787                        ctx.interrupt_emitter(ir::Expression::SubgroupBallotResult, function_span)?;
3788                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3789                    rctx.block.push(
3790                        ir::Statement::SubgroupBallot { result, predicate },
3791                        function_span,
3792                    );
3793                    return Ok(Some((result, MustUse::Yes)));
3794                }
3795                "quadSwapX" => {
3796                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3797
3798                    let argument = self.expression(args.next()?, ctx)?;
3799                    args.finish()?;
3800
3801                    let ty = ctx.register_type(argument)?;
3802
3803                    let result = ctx.interrupt_emitter(
3804                        crate::Expression::SubgroupOperationResult { ty },
3805                        function_span,
3806                    )?;
3807                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3808                    rctx.block.push(
3809                        crate::Statement::SubgroupGather {
3810                            mode: crate::GatherMode::QuadSwap(crate::Direction::X),
3811                            argument,
3812                            result,
3813                        },
3814                        function_span,
3815                    );
3816                    return Ok(Some((result, MustUse::Yes)));
3817                }
3818                "quadSwapY" => {
3819                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3820
3821                    let argument = self.expression(args.next()?, ctx)?;
3822                    args.finish()?;
3823
3824                    let ty = ctx.register_type(argument)?;
3825
3826                    let result = ctx.interrupt_emitter(
3827                        crate::Expression::SubgroupOperationResult { ty },
3828                        function_span,
3829                    )?;
3830                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3831                    rctx.block.push(
3832                        crate::Statement::SubgroupGather {
3833                            mode: crate::GatherMode::QuadSwap(crate::Direction::Y),
3834                            argument,
3835                            result,
3836                        },
3837                        function_span,
3838                    );
3839                    return Ok(Some((result, MustUse::Yes)));
3840                }
3841                "quadSwapDiagonal" => {
3842                    let mut args = ctx.prepare_args(arguments, 1, function_span);
3843
3844                    let argument = self.expression(args.next()?, ctx)?;
3845                    args.finish()?;
3846
3847                    let ty = ctx.register_type(argument)?;
3848
3849                    let result = ctx.interrupt_emitter(
3850                        crate::Expression::SubgroupOperationResult { ty },
3851                        function_span,
3852                    )?;
3853                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3854                    rctx.block.push(
3855                        crate::Statement::SubgroupGather {
3856                            mode: crate::GatherMode::QuadSwap(crate::Direction::Diagonal),
3857                            argument,
3858                            result,
3859                        },
3860                        function_span,
3861                    );
3862                    return Ok(Some((result, MustUse::Yes)));
3863                }
3864                "coopStore" | "coopStoreT" => {
3865                    let row_major = function_name.ends_with("T");
3866
3867                    let mut args = ctx.prepare_args(arguments, 2, function_span);
3868                    let target = self.expression(args.next()?, ctx)?;
3869                    let pointer = self.expression(args.next()?, ctx)?;
3870                    let stride = if args.total_args > 2 {
3871                        self.expression(args.next()?, ctx)?
3872                    } else {
3873                        // Infer the stride from the matrix type
3874                        let stride = match *resolve_inner!(ctx, target) {
3875                            ir::TypeInner::CooperativeMatrix { columns, rows, .. } => {
3876                                if row_major {
3877                                    columns as u32
3878                                } else {
3879                                    rows as u32
3880                                }
3881                            }
3882                            _ => 0,
3883                        };
3884                        ctx.append_expression(
3885                            ir::Expression::Literal(ir::Literal::U32(stride)),
3886                            Span::UNDEFINED,
3887                        )?
3888                    };
3889                    args.finish()?;
3890
3891                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3892                    rctx.block.push(
3893                        crate::Statement::CooperativeStore {
3894                            target,
3895                            data: crate::CooperativeData {
3896                                pointer,
3897                                stride,
3898                                row_major,
3899                            },
3900                        },
3901                        function_span,
3902                    );
3903                    return Ok(None);
3904                }
3905                "coopMultiplyAdd" => {
3906                    let mut args = ctx.prepare_args(arguments, 3, function_span);
3907                    let a = self.expression(args.next()?, ctx)?;
3908                    let b = self.expression(args.next()?, ctx)?;
3909                    let c = self.expression(args.next()?, ctx)?;
3910                    args.finish()?;
3911
3912                    (
3913                        ir::Expression::CooperativeMultiplyAdd { a, b, c },
3914                        MustUse::Yes,
3915                    )
3916                }
3917                "traceRay" => {
3918                    let mut args = ctx.prepare_args(arguments, 3, function_span);
3919                    let acceleration_structure = self.expression(args.next()?, ctx)?;
3920                    let descriptor = self.expression(args.next()?, ctx)?;
3921                    let payload = self.expression(args.next()?, ctx)?;
3922                    args.finish()?;
3923
3924                    let _ = ctx.module.generate_ray_desc_type();
3925                    let fun = ir::RayPipelineFunction::TraceRay {
3926                        acceleration_structure,
3927                        descriptor,
3928                        payload,
3929                    };
3930
3931                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3932                    rctx.block
3933                        .extend(rctx.emitter.finish(&rctx.function.expressions));
3934                    rctx.emitter.start(&rctx.function.expressions);
3935                    rctx.block
3936                        .push(ir::Statement::RayPipelineFunction(fun), function_span);
3937                    return Ok(None);
3938                }
3939                "debugPrintf" => {
3940                    if !ctx
3941                        .enable_extensions
3942                        .contains(crate::front::wgsl::ImplementedEnableExtension::WgpuDebugPrintf)
3943                    {
3944                        return Err(Box::new(Error::EnableExtensionNotEnabled {
3945                            span: function_span,
3946                            kind: crate::front::wgsl::ImplementedEnableExtension::WgpuDebugPrintf
3947                                .into(),
3948                        }));
3949                    }
3950
3951                    if arguments.is_empty() {
3952                        return Err(Box::new(Error::WrongArgumentCount {
3953                            expected: 1..u32::MAX,
3954                            found: 0,
3955                            span: function_span,
3956                        }));
3957                    }
3958
3959                    // extract the format string
3960                    let format_handle = arguments[0];
3961                    let format = match ctx.ast_expressions[format_handle] {
3962                        ast::Expression::String(s) => s.to_string(),
3963                        _ => {
3964                            return Err(Box::new(Error::ExpectedStringLiteral {
3965                                span: ctx.ast_expressions.get_span(format_handle),
3966                                description:
3967                                    "debugPrintf's first argument must be a string literal",
3968                            }))
3969                        }
3970                    };
3971
3972                    // extract remaining arguments (if any)
3973                    let mut ir_arguments = Vec::with_capacity(arguments.len().saturating_sub(1));
3974
3975                    for &ast_handle in &arguments[1..] {
3976                        let ir_handle = self.expression(ast_handle, ctx)?;
3977                        ir_arguments.push(ir_handle);
3978                    }
3979
3980                    let rctx = ctx.runtime_expression_ctx(function_span)?;
3981                    rctx.block
3982                        .extend(rctx.emitter.finish(&rctx.function.expressions));
3983                    rctx.emitter.start(&rctx.function.expressions);
3984                    rctx.block.push(
3985                        ir::Statement::DebugPrintf {
3986                            format,
3987                            arguments: ir_arguments,
3988                        },
3989                        function_span,
3990                    );
3991
3992                    return Ok(None);
3993                }
3994                _ => return Err(Box::new(Error::UnknownIdent(function_span, function_name))),
3995            }
3996        };
3997
3998        let expr = ctx.append_expression(expr, function_span)?;
3999        Ok(Some((expr, must_use)))
4000    }
4001
4002    /// Generate Naga IR for call expressions and statements, and type
4003    /// constructor expressions.
4004    ///
4005    /// The "function" being called is simply an `Ident` that we know refers to
4006    /// some module-scope definition.
4007    ///
4008    /// - If it is the name of a type, then the expression is a type constructor
4009    ///   expression: either constructing a value from components, a conversion
4010    ///   expression, or a zero value expression.
4011    ///
4012    /// - If it is the name of a function, then we're generating a [`Call`]
4013    ///   statement. We may be in the midst of generating code for an
4014    ///   expression, in which case we must generate an `Emit` statement to
4015    ///   force evaluation of the IR expressions we've generated so far, add the
4016    ///   `Call` statement to the current block, and then resume generating
4017    ///   expressions.
4018    ///
4019    /// [`Call`]: ir::Statement::Call
4020    fn call(
4021        &mut self,
4022        call_phrase: &ast::CallPhrase<'source>,
4023        span: Span,
4024        ctx: &mut ExpressionContext<'source, '_, '_>,
4025        is_statement: bool,
4026    ) -> Result<'source, Option<Handle<ir::Expression>>> {
4027        let function_name = match call_phrase.function.ident {
4028            ast::IdentExpr::Unresolved(name) => name,
4029            ast::IdentExpr::Local(_) => {
4030                return Err(Box::new(Error::CalledLocalDecl(
4031                    call_phrase.function.ident_span,
4032                )))
4033            }
4034        };
4035        let mut function_span = call_phrase.function.ident_span;
4036        function_span.subsume(call_phrase.function.template_list_span);
4037        let arguments = call_phrase.arguments.as_slice();
4038
4039        let mut tl = TemplateListIter::new(function_span, &call_phrase.function.template_list);
4040
4041        let result = match ctx.globals.get(function_name) {
4042            Some(&LoweredGlobalDecl::Type(ty)) => {
4043                // user-declared types can't make use of template lists
4044                tl.finish(ctx)?;
4045
4046                let handle =
4047                    self.construct(span, Constructor::Type(ty), function_span, arguments, ctx)?;
4048                Some((handle, MustUse::Yes))
4049            }
4050            Some(
4051                &LoweredGlobalDecl::Const(_)
4052                | &LoweredGlobalDecl::Override(_)
4053                | &LoweredGlobalDecl::Var(_),
4054            ) => {
4055                return Err(Box::new(Error::Unexpected(
4056                    function_span,
4057                    ExpectedToken::Function,
4058                )))
4059            }
4060            Some(&LoweredGlobalDecl::EntryPoint(_)) => {
4061                return Err(Box::new(Error::CalledEntryPoint(function_span)));
4062            }
4063            Some(&LoweredGlobalDecl::Function {
4064                handle: function,
4065                must_use,
4066            }) => {
4067                // user-declared functions can't make use of template lists
4068                tl.finish(ctx)?;
4069
4070                let arguments = arguments
4071                    .iter()
4072                    .enumerate()
4073                    .map(|(i, &arg)| {
4074                        // Try to convert abstract values to the known argument types
4075                        let Some(&ir::FunctionArgument {
4076                            ty: parameter_ty, ..
4077                        }) = ctx.module.functions[function].arguments.get(i)
4078                        else {
4079                            // Wrong number of arguments... just concretize the type here
4080                            // and let the validator report the error.
4081                            return self.expression(arg, ctx);
4082                        };
4083
4084                        let expr = self.expression_for_abstract(arg, ctx)?;
4085                        ctx.try_automatic_conversions(
4086                            expr,
4087                            &proc::TypeResolution::Handle(parameter_ty),
4088                            ctx.ast_expressions.get_span(arg),
4089                        )
4090                    })
4091                    .collect::<Result<Vec<_>>>()?;
4092
4093                let has_result = ctx.module.functions[function].result.is_some();
4094
4095                let rctx = ctx.runtime_expression_ctx(span)?;
4096                // we need to always do this before a fn call since all arguments need to be emitted before the fn call
4097                rctx.block
4098                    .extend(rctx.emitter.finish(&rctx.function.expressions));
4099                let result = has_result.then(|| {
4100                    let result = rctx
4101                        .function
4102                        .expressions
4103                        .append(ir::Expression::CallResult(function), span);
4104                    rctx.local_expression_kind_tracker
4105                        .insert(result, proc::ExpressionKind::Runtime);
4106                    (result, must_use.into())
4107                });
4108                rctx.emitter.start(&rctx.function.expressions);
4109                rctx.block.push(
4110                    ir::Statement::Call {
4111                        function,
4112                        arguments,
4113                        result: result.map(|(expr, _)| expr),
4114                    },
4115                    span,
4116                );
4117
4118                result
4119            }
4120            None => {
4121                // If the name refers to a predeclared type, this is a construction expression.
4122                let ty = conv::map_predeclared_type(
4123                    &ctx.enable_extensions,
4124                    function_span,
4125                    function_name,
4126                )?;
4127                if let Some(ty) = ty {
4128                    let empty_template_list = call_phrase.function.template_list.is_empty();
4129                    let constructor_ty = match ty {
4130                        conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Vector {
4131                            size,
4132                        }) if empty_template_list => Constructor::PartialVector { size },
4133                        conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Matrix {
4134                            columns,
4135                            rows,
4136                        }) if empty_template_list => Constructor::PartialMatrix { columns, rows },
4137                        conv::PredeclaredType::TypeGenerator(conv::TypeGenerator::Array)
4138                            if empty_template_list =>
4139                        {
4140                            Constructor::PartialArray
4141                        }
4142                        conv::PredeclaredType::TypeGenerator(
4143                            conv::TypeGenerator::CooperativeMatrix { .. },
4144                        ) if empty_template_list => {
4145                            return Err(Box::new(Error::UnderspecifiedCooperativeMatrix));
4146                        }
4147                        _ => Constructor::Type(self.finalize_type(ctx, ty, &mut tl, None)?),
4148                    };
4149                    tl.finish(ctx)?;
4150                    let handle =
4151                        self.construct(span, constructor_ty, function_span, arguments, ctx)?;
4152                    Some((handle, MustUse::Yes))
4153                } else {
4154                    // Otherwise, it must be a call to a builtin function.
4155                    let result = self.call_builtin(
4156                        function_name,
4157                        function_span,
4158                        arguments,
4159                        &mut tl,
4160                        span,
4161                        ctx,
4162                        is_statement,
4163                    )?;
4164                    tl.finish(ctx)?;
4165                    result
4166                }
4167            }
4168        };
4169
4170        let result_used = !is_statement;
4171        if matches!(result, Some((_, MustUse::Yes))) && !result_used {
4172            return Err(Box::new(Error::FunctionMustUseUnused(function_span)));
4173        }
4174        Ok(result.map(|(expr, _)| expr))
4175    }
4176
4177    /// Generate a Naga IR [`Math`] expression.
4178    ///
4179    /// Generate Naga IR for a call to the [`MathFunction`] `fun`, whose
4180    /// unlowered arguments are `ast_arguments`.
4181    ///
4182    /// The `span` argument should give the span of the function name in the
4183    /// call expression.
4184    ///
4185    /// [`Math`]: ir::Expression::Math
4186    /// [`MathFunction`]: ir::MathFunction
4187    fn math_function_helper(
4188        &mut self,
4189        span: Span,
4190        fun: ir::MathFunction,
4191        ast_arguments: &[Handle<ast::Expression<'source>>],
4192        ctx: &mut ExpressionContext<'source, '_, '_>,
4193    ) -> Result<'source, ir::Expression> {
4194        let mut lowered_arguments = Vec::with_capacity(ast_arguments.len());
4195        for &arg in ast_arguments {
4196            let lowered = self.expression_for_abstract(arg, ctx)?;
4197            ctx.grow_types(lowered)?;
4198            lowered_arguments.push(lowered);
4199        }
4200
4201        let fun_overloads = fun.overloads();
4202        let rule = self.resolve_overloads(span, fun, fun_overloads, &lowered_arguments, ctx)?;
4203        self.apply_automatic_conversions_for_call(&rule, &mut lowered_arguments, ctx)?;
4204
4205        // If this function returns a predeclared type, register it
4206        // in `Module::special_types`. The typifier will expect to
4207        // be able to find it there.
4208        if let proc::Conclusion::Predeclared(predeclared) = rule.conclusion {
4209            ctx.module.generate_predeclared_type(predeclared);
4210        }
4211
4212        Ok(ir::Expression::Math {
4213            fun,
4214            arg: lowered_arguments[0],
4215            arg1: lowered_arguments.get(1).cloned(),
4216            arg2: lowered_arguments.get(2).cloned(),
4217            arg3: lowered_arguments.get(3).cloned(),
4218        })
4219    }
4220
4221    /// Choose the right overload for a function call.
4222    ///
4223    /// Return a [`Rule`] representing the most preferred overload in
4224    /// `overloads` to apply to `arguments`, or return an error explaining why
4225    /// the call is not valid.
4226    ///
4227    /// Use `fun` to identify the function being called in error messages;
4228    /// `span` should be the span of the function name in the call expression.
4229    ///
4230    /// [`Rule`]: proc::Rule
4231    fn resolve_overloads<O, F>(
4232        &self,
4233        span: Span,
4234        fun: F,
4235        overloads: O,
4236        arguments: &[Handle<ir::Expression>],
4237        ctx: &ExpressionContext<'source, '_, '_>,
4238    ) -> Result<'source, proc::Rule>
4239    where
4240        O: proc::OverloadSet,
4241        F: TryToWgsl + core::fmt::Debug + Copy,
4242    {
4243        let mut remaining_overloads = overloads.clone();
4244        let min_arguments = remaining_overloads.min_arguments();
4245        let max_arguments = remaining_overloads.max_arguments();
4246        if arguments.len() < min_arguments {
4247            return Err(Box::new(Error::WrongArgumentCount {
4248                span,
4249                expected: min_arguments as u32..max_arguments as u32,
4250                found: arguments.len() as u32,
4251            }));
4252        }
4253        if arguments.len() > max_arguments {
4254            return Err(Box::new(Error::TooManyArguments {
4255                function: fun.to_wgsl_for_diagnostics(),
4256                call_span: span,
4257                arg_span: ctx.get_expression_span(arguments[max_arguments]),
4258                max_arguments: max_arguments as _,
4259            }));
4260        }
4261
4262        log::debug!(
4263            "Initial overloads: {:#?}",
4264            remaining_overloads.for_debug(&ctx.module.types)
4265        );
4266
4267        for (arg_index, &arg) in arguments.iter().enumerate() {
4268            let arg_type_resolution = &ctx.typifier()[arg];
4269            let arg_inner = arg_type_resolution.inner_with(&ctx.module.types);
4270            log::debug!(
4271                "Supplying argument {arg_index} of type {:?}",
4272                arg_type_resolution.for_debug(&ctx.module.types)
4273            );
4274            let next_remaining_overloads =
4275                remaining_overloads.arg(arg_index, arg_inner, &ctx.module.types);
4276
4277            // If any argument is not a constant expression, then no overloads
4278            // that accept abstract values should be considered.
4279            // (`OverloadSet::concrete_only` is supposed to help impose this
4280            // restriction.) However, no `MathFunction` accepts a mix of
4281            // abstract and concrete arguments, so we don't need to worry
4282            // about that here.
4283
4284            log::debug!(
4285                "Remaining overloads: {:#?}",
4286                next_remaining_overloads.for_debug(&ctx.module.types)
4287            );
4288
4289            // If the set of remaining overloads is empty, then this argument's type
4290            // was unacceptable. Diagnose the problem and produce an error message.
4291            if next_remaining_overloads.is_empty() {
4292                let function = fun.to_wgsl_for_diagnostics();
4293                let call_span = span;
4294                let arg_span = ctx.get_expression_span(arg);
4295                let arg_ty = ctx.as_diagnostic_display(arg_type_resolution).to_string();
4296
4297                // Is this type *ever* permitted for the arg_index'th argument?
4298                // For example, `bool` is never permitted for `max`.
4299                let only_this_argument = overloads.arg(arg_index, arg_inner, &ctx.module.types);
4300                if only_this_argument.is_empty() {
4301                    // No overload of `fun` accepts this type as the
4302                    // arg_index'th argument. Determine the set of types that
4303                    // would ever be allowed there.
4304                    let allowed: Vec<String> = overloads
4305                        .allowed_args(arg_index, &ctx.module.to_ctx())
4306                        .iter()
4307                        .map(|ty| ctx.type_resolution_to_string(ty))
4308                        .collect();
4309
4310                    if allowed.is_empty() {
4311                        // No overload of `fun` accepts any argument at this
4312                        // index, so it's a simple case of excess arguments.
4313                        // However, since each `MathFunction`'s overloads all
4314                        // have the same arity, we should have detected this
4315                        // earlier.
4316                        unreachable!("expected all overloads to have the same arity");
4317                    }
4318
4319                    // Some overloads of `fun` do accept this many arguments,
4320                    // but none accept one of this type.
4321                    return Err(Box::new(Error::WrongArgumentType {
4322                        function,
4323                        call_span,
4324                        arg_span,
4325                        arg_index: arg_index as u32,
4326                        arg_ty,
4327                        allowed,
4328                    }));
4329                }
4330
4331                // This argument's type is accepted by some overloads---just
4332                // not those overloads that remain, given the prior arguments.
4333                // For example, `max` accepts `f32` as its second argument -
4334                // but not if the first was `i32`.
4335
4336                // Build a list of the types that would have been accepted here,
4337                // given the prior arguments.
4338                let allowed: Vec<String> = remaining_overloads
4339                    .allowed_args(arg_index, &ctx.module.to_ctx())
4340                    .iter()
4341                    .map(|ty| ctx.type_resolution_to_string(ty))
4342                    .collect();
4343
4344                // Re-run the argument list to determine which prior argument
4345                // made this one unacceptable.
4346                let mut remaining_overloads = overloads;
4347                for (prior_index, &prior_expr) in arguments.iter().enumerate() {
4348                    let prior_type_resolution = &ctx.typifier()[prior_expr];
4349                    let prior_ty = prior_type_resolution.inner_with(&ctx.module.types);
4350                    remaining_overloads =
4351                        remaining_overloads.arg(prior_index, prior_ty, &ctx.module.types);
4352                    if remaining_overloads
4353                        .arg(arg_index, arg_inner, &ctx.module.types)
4354                        .is_empty()
4355                    {
4356                        // This is the argument that killed our dreams.
4357                        let inconsistent_span = ctx.get_expression_span(arguments[prior_index]);
4358                        let inconsistent_ty =
4359                            ctx.as_diagnostic_display(prior_type_resolution).to_string();
4360
4361                        if allowed.is_empty() {
4362                            // Some overloads did accept `ty` at `arg_index`, but
4363                            // given the arguments up through `prior_expr`, we see
4364                            // no types acceptable at `arg_index`. This means that some
4365                            // overloads expect fewer arguments than others. However,
4366                            // each `MathFunction`'s overloads have the same arity, so this
4367                            // should be impossible.
4368                            unreachable!("expected all overloads to have the same arity");
4369                        }
4370
4371                        // Report `arg`'s type as inconsistent with `prior_expr`'s
4372                        return Err(Box::new(Error::InconsistentArgumentType {
4373                            function,
4374                            call_span,
4375                            arg_span,
4376                            arg_index: arg_index as u32,
4377                            arg_ty,
4378                            inconsistent_span,
4379                            inconsistent_index: prior_index as u32,
4380                            inconsistent_ty,
4381                            allowed,
4382                        }));
4383                    }
4384                }
4385                unreachable!("Failed to eliminate argument type when re-tried");
4386            }
4387            remaining_overloads = next_remaining_overloads;
4388        }
4389
4390        // Select the most preferred type rule for this call,
4391        // given the argument types supplied above.
4392        Ok(remaining_overloads.most_preferred())
4393    }
4394
4395    /// Apply automatic type conversions for a function call.
4396    ///
4397    /// Apply whatever automatic conversions are needed to pass `arguments` to
4398    /// the function overload described by `rule`. Update `arguments` to refer
4399    /// to the converted arguments.
4400    fn apply_automatic_conversions_for_call(
4401        &self,
4402        rule: &proc::Rule,
4403        arguments: &mut [Handle<ir::Expression>],
4404        ctx: &mut ExpressionContext<'source, '_, '_>,
4405    ) -> Result<'source, ()> {
4406        for (i, argument) in arguments.iter_mut().enumerate() {
4407            let goal_inner = rule.arguments[i].inner_with(&ctx.module.types);
4408            let converted = match goal_inner.scalar_for_conversions(&ctx.module.types) {
4409                Some(goal_scalar) => {
4410                    let arg_span = ctx.get_expression_span(*argument);
4411                    ctx.try_automatic_conversion_for_leaf_scalar(*argument, goal_scalar, arg_span)?
4412                }
4413                // No conversion is necessary.
4414                None => *argument,
4415            };
4416
4417            *argument = converted;
4418        }
4419
4420        Ok(())
4421    }
4422
4423    fn atomic_pointer(
4424        &mut self,
4425        expr: Handle<ast::Expression<'source>>,
4426        ctx: &mut ExpressionContext<'source, '_, '_>,
4427    ) -> Result<'source, (Handle<ir::Expression>, ir::Scalar)> {
4428        let span = ctx.ast_expressions.get_span(expr);
4429        let pointer = self.expression(expr, ctx)?;
4430
4431        match *resolve_inner!(ctx, pointer) {
4432            ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
4433                ir::TypeInner::Atomic(scalar) => Ok((pointer, scalar)),
4434                ref other => {
4435                    log::error!("Pointer type to {other:?} passed to atomic op");
4436                    Err(Box::new(Error::InvalidAtomicPointer(span)))
4437                }
4438            },
4439            ref other => {
4440                log::error!("Type {other:?} passed to atomic op");
4441                Err(Box::new(Error::InvalidAtomicPointer(span)))
4442            }
4443        }
4444    }
4445
4446    fn atomic_helper(
4447        &mut self,
4448        span: Span,
4449        fun: ir::AtomicFunction,
4450        args: &[Handle<ast::Expression<'source>>],
4451        is_statement: bool,
4452        ctx: &mut ExpressionContext<'source, '_, '_>,
4453    ) -> Result<'source, Option<Handle<ir::Expression>>> {
4454        let mut args = ctx.prepare_args(args, 2, span);
4455
4456        let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?;
4457        let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?;
4458        let value_inner = resolve_inner!(ctx, value);
4459        args.finish()?;
4460
4461        // If we don't use the return value of a 64-bit `min` or `max`
4462        // operation, generate a no-result form of the `Atomic` statement, so
4463        // that we can pass validation with only `SHADER_INT64_ATOMIC_MIN_MAX`
4464        // whenever possible.
4465        let is_64_bit_min_max = matches!(fun, ir::AtomicFunction::Min | ir::AtomicFunction::Max)
4466            && matches!(
4467                *value_inner,
4468                ir::TypeInner::Scalar(ir::Scalar { width: 8, .. })
4469            );
4470        let result = if is_64_bit_min_max && is_statement {
4471            let rctx = ctx.runtime_expression_ctx(span)?;
4472            rctx.block
4473                .extend(rctx.emitter.finish(&rctx.function.expressions));
4474            rctx.emitter.start(&rctx.function.expressions);
4475            None
4476        } else {
4477            let ty = ctx.register_type(value)?;
4478            Some(ctx.interrupt_emitter(
4479                ir::Expression::AtomicResult {
4480                    ty,
4481                    comparison: false,
4482                },
4483                span,
4484            )?)
4485        };
4486        let rctx = ctx.runtime_expression_ctx(span)?;
4487        rctx.block.push(
4488            ir::Statement::Atomic {
4489                pointer,
4490                fun,
4491                value,
4492                result,
4493            },
4494            span,
4495        );
4496        Ok(result)
4497    }
4498
4499    fn texture_sample_helper(
4500        &mut self,
4501        fun: Texture,
4502        args: &[Handle<ast::Expression<'source>>],
4503        span: Span,
4504        ctx: &mut ExpressionContext<'source, '_, '_>,
4505    ) -> Result<'source, ir::Expression> {
4506        let mut args = ctx.prepare_args(args, fun.min_argument_count(), span);
4507
4508        fn get_image_and_span<'source>(
4509            lowerer: &mut Lowerer<'source, '_>,
4510            args: &mut ArgumentContext<'_, 'source>,
4511            ctx: &mut ExpressionContext<'source, '_, '_>,
4512        ) -> Result<'source, (Handle<ir::Expression>, Span)> {
4513            let image = args.next()?;
4514            let image_span = ctx.ast_expressions.get_span(image);
4515            let image = lowerer.expression_for_abstract(image, ctx)?;
4516            Ok((image, image_span))
4517        }
4518
4519        let image;
4520        let image_span;
4521        let gather;
4522        match fun {
4523            Texture::Gather => {
4524                let image_or_component = args.next()?;
4525                let image_or_component_span = ctx.ast_expressions.get_span(image_or_component);
4526                // Gathers from depth textures don't take an initial `component` argument.
4527                let lowered_image_or_component = self.expression(image_or_component, ctx)?;
4528
4529                match *resolve_inner!(ctx, lowered_image_or_component) {
4530                    ir::TypeInner::Image {
4531                        class: ir::ImageClass::Depth { .. },
4532                        ..
4533                    } => {
4534                        image = lowered_image_or_component;
4535                        image_span = image_or_component_span;
4536                        gather = Some(ir::SwizzleComponent::X);
4537                    }
4538                    _ => {
4539                        (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4540                        gather = Some(ctx.gather_component(
4541                            lowered_image_or_component,
4542                            image_or_component_span,
4543                            span,
4544                        )?);
4545                    }
4546                }
4547            }
4548            Texture::GatherCompare => {
4549                (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4550                gather = Some(ir::SwizzleComponent::X);
4551            }
4552
4553            _ => {
4554                (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
4555                gather = None;
4556            }
4557        };
4558
4559        let sampler = self.expression_for_abstract(args.next()?, ctx)?;
4560
4561        let coordinate = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4562        let clamp_to_edge = matches!(fun, Texture::SampleBaseClampToEdge);
4563
4564        let (class, arrayed) = ctx.image_data(image, image_span)?;
4565        let array_index = arrayed
4566            .then(|| self.expression(args.next()?, ctx))
4567            .transpose()?;
4568
4569        let level;
4570        let depth_ref;
4571        match fun {
4572            Texture::Gather => {
4573                level = ir::SampleLevel::Zero;
4574                depth_ref = None;
4575            }
4576            Texture::GatherCompare => {
4577                let reference =
4578                    self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4579                level = ir::SampleLevel::Zero;
4580                depth_ref = Some(reference);
4581            }
4582
4583            Texture::Sample => {
4584                level = ir::SampleLevel::Auto;
4585                depth_ref = None;
4586            }
4587            Texture::SampleBias => {
4588                let bias = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4589                level = ir::SampleLevel::Bias(bias);
4590                depth_ref = None;
4591            }
4592            Texture::SampleCompare => {
4593                let reference =
4594                    self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4595                level = ir::SampleLevel::Auto;
4596                depth_ref = Some(reference);
4597            }
4598            Texture::SampleCompareLevel => {
4599                let reference =
4600                    self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4601                level = ir::SampleLevel::Zero;
4602                depth_ref = Some(reference);
4603            }
4604            Texture::SampleGrad => {
4605                let x = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4606                let y = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?;
4607                level = ir::SampleLevel::Gradient { x, y };
4608                depth_ref = None;
4609            }
4610            Texture::SampleLevel => {
4611                let exact = match class {
4612                    // When applied to depth textures, `textureSampleLevel`'s
4613                    // `level` argument is an `i32` or `u32`.
4614                    ir::ImageClass::Depth { .. } => self.expression(args.next()?, ctx)?,
4615
4616                    // When applied to other sampled types, its `level` argument
4617                    // is an `f32`.
4618                    ir::ImageClass::Sampled { .. } => {
4619                        self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?
4620                    }
4621
4622                    // Sampling `External` textures with a specified level isn't
4623                    // allowed, and sampling `Storage` textures isn't allowed at
4624                    // all. Let the validator report the error.
4625                    ir::ImageClass::Storage { .. } | ir::ImageClass::External => {
4626                        self.expression(args.next()?, ctx)?
4627                    }
4628                };
4629                level = ir::SampleLevel::Exact(exact);
4630                depth_ref = None;
4631            }
4632            Texture::SampleBaseClampToEdge => {
4633                level = crate::SampleLevel::Zero;
4634                depth_ref = None;
4635            }
4636        };
4637
4638        let offset = args
4639            .next()
4640            .map(|arg| self.expression_with_leaf_scalar(arg, ir::Scalar::I32, &mut ctx.as_const()))
4641            .ok()
4642            .transpose()?;
4643
4644        args.finish()?;
4645
4646        Ok(ir::Expression::ImageSample {
4647            image,
4648            sampler,
4649            gather,
4650            coordinate,
4651            array_index,
4652            offset,
4653            level,
4654            depth_ref,
4655            clamp_to_edge,
4656        })
4657    }
4658
4659    fn subgroup_operation_helper(
4660        &mut self,
4661        span: Span,
4662        op: ir::SubgroupOperation,
4663        collective_op: ir::CollectiveOperation,
4664        arguments: &[Handle<ast::Expression<'source>>],
4665        ctx: &mut ExpressionContext<'source, '_, '_>,
4666    ) -> Result<'source, Handle<ir::Expression>> {
4667        let mut args = ctx.prepare_args(arguments, 1, span);
4668
4669        let argument = self.expression(args.next()?, ctx)?;
4670        args.finish()?;
4671
4672        let ty = ctx.register_type(argument)?;
4673
4674        let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
4675        let rctx = ctx.runtime_expression_ctx(span)?;
4676        rctx.block.push(
4677            ir::Statement::SubgroupCollectiveOperation {
4678                op,
4679                collective_op,
4680                argument,
4681                result,
4682            },
4683            span,
4684        );
4685        Ok(result)
4686    }
4687
4688    fn subgroup_gather_helper(
4689        &mut self,
4690        span: Span,
4691        mode: SubgroupGather,
4692        arguments: &[Handle<ast::Expression<'source>>],
4693        ctx: &mut ExpressionContext<'source, '_, '_>,
4694    ) -> Result<'source, Handle<ir::Expression>> {
4695        let mut args = ctx.prepare_args(arguments, 2, span);
4696
4697        let argument = self.expression(args.next()?, ctx)?;
4698
4699        use SubgroupGather as Sg;
4700        let mode = if let Sg::BroadcastFirst = mode {
4701            ir::GatherMode::BroadcastFirst
4702        } else {
4703            let index = self.expression(args.next()?, ctx)?;
4704            match mode {
4705                Sg::BroadcastFirst => unreachable!(),
4706                Sg::Broadcast => ir::GatherMode::Broadcast(index),
4707                Sg::Shuffle => ir::GatherMode::Shuffle(index),
4708                Sg::ShuffleDown => ir::GatherMode::ShuffleDown(index),
4709                Sg::ShuffleUp => ir::GatherMode::ShuffleUp(index),
4710                Sg::ShuffleXor => ir::GatherMode::ShuffleXor(index),
4711                Sg::QuadBroadcast => ir::GatherMode::QuadBroadcast(index),
4712            }
4713        };
4714
4715        args.finish()?;
4716
4717        let ty = ctx.register_type(argument)?;
4718
4719        let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?;
4720        let rctx = ctx.runtime_expression_ctx(span)?;
4721        rctx.block.push(
4722            ir::Statement::SubgroupGather {
4723                mode,
4724                argument,
4725                result,
4726            },
4727            span,
4728        );
4729        Ok(result)
4730    }
4731
4732    fn r#struct(
4733        &mut self,
4734        s: &ast::Struct<'source>,
4735        span: Span,
4736        ctx: &mut GlobalContext<'source, '_, '_>,
4737    ) -> Result<'source, Handle<ir::Type>> {
4738        let mut offset = 0;
4739        let mut struct_alignment = proc::Alignment::ONE;
4740        let mut members = Vec::with_capacity(s.members.len());
4741
4742        let mut doc_comments: Vec<Option<Vec<String>>> = Vec::new();
4743
4744        for member in s.members.iter() {
4745            let ty = self.resolve_ast_type(&member.ty, &mut ctx.as_const())?;
4746
4747            ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| {
4748                let LayoutErrorInner::TooLarge = err.inner else {
4749                    unreachable!("unexpected layout error: {err:?}");
4750                };
4751                // Since anonymous types of struct members don't get a span,
4752                // associate the error with the member. The layouter could have
4753                // failed on any type that was pending layout, but if it wasn't
4754                // the current struct member, it wasn't a struct member at all,
4755                // because we resolve struct members one-by-one.
4756                if ty == err.ty {
4757                    Box::new(Error::StructMemberTooLarge {
4758                        member_name_span: member.name.span,
4759                    })
4760                } else {
4761                    // Lots of type definitions don't get spans, so this error
4762                    // message may not be very useful.
4763                    Box::new(Error::TypeTooLarge {
4764                        span: ctx.module.types.get_span(err.ty),
4765                    })
4766                }
4767            })?;
4768
4769            let member_min_size = ctx.layouter[ty].size;
4770            let member_min_alignment = ctx.layouter[ty].alignment;
4771
4772            let member_size = if let Some(size_expr) = member.size {
4773                let (size, span) = self.const_u32(size_expr, &mut ctx.as_const())?;
4774                if let ir::TypeInner::Array {
4775                    size: ir::ArraySize::Dynamic | ir::ArraySize::Pending(_),
4776                    ..
4777                } = ctx.module.types[ty].inner
4778                {
4779                    return Err(Box::new(Error::SizeAttributeRequiresFixedFootprint(span)));
4780                }
4781                if size < member_min_size {
4782                    return Err(Box::new(Error::SizeAttributeTooLow(span, member_min_size)));
4783                } else {
4784                    size
4785                }
4786            } else {
4787                member_min_size
4788            };
4789
4790            let member_alignment = if let Some(align_expr) = member.align {
4791                let (align, span) = self.const_u32(align_expr, &mut ctx.as_const())?;
4792                if let Some(alignment) = proc::Alignment::new(align) {
4793                    if alignment < member_min_alignment {
4794                        return Err(Box::new(Error::AlignAttributeTooLow(
4795                            span,
4796                            member_min_alignment,
4797                        )));
4798                    } else {
4799                        alignment
4800                    }
4801                } else {
4802                    return Err(Box::new(Error::NonPowerOfTwoAlignAttribute(span)));
4803                }
4804            } else {
4805                member_min_alignment
4806            };
4807
4808            let binding = self.binding(&member.binding, ty, ctx)?;
4809
4810            offset = member_alignment.round_up(offset);
4811            struct_alignment = struct_alignment.max(member_alignment);
4812
4813            if !member.doc_comments.is_empty() {
4814                doc_comments.push(Some(
4815                    member.doc_comments.iter().map(|s| s.to_string()).collect(),
4816                ));
4817            } else {
4818                doc_comments.push(None);
4819            }
4820            members.push(ir::StructMember {
4821                name: Some(member.name.name.to_owned()),
4822                ty,
4823                binding,
4824                offset,
4825            });
4826
4827            offset += member_size;
4828            if offset > crate::valid::MAX_TYPE_SIZE {
4829                return Err(Box::new(Error::TypeTooLarge { span }));
4830            }
4831        }
4832
4833        let size = struct_alignment.round_up(offset);
4834        let inner = ir::TypeInner::Struct {
4835            members,
4836            span: size,
4837        };
4838
4839        let handle = ctx.module.types.insert(
4840            ir::Type {
4841                name: Some(s.name.name.to_string()),
4842                inner,
4843            },
4844            span,
4845        );
4846        for (i, c) in doc_comments.drain(..).enumerate() {
4847            if let Some(comment) = c {
4848                ctx.module
4849                    .get_or_insert_default_doc_comments()
4850                    .struct_members
4851                    .insert((handle, i), comment);
4852            }
4853        }
4854        Ok(handle)
4855    }
4856
4857    fn const_u32(
4858        &mut self,
4859        expr: Handle<ast::Expression<'source>>,
4860        ctx: &mut ExpressionContext<'source, '_, '_>,
4861    ) -> Result<'source, (u32, Span)> {
4862        let span = ctx.ast_expressions.get_span(expr);
4863        let expr = self.expression(expr, ctx)?;
4864        let value = ctx
4865            .module
4866            .to_ctx()
4867            .get_const_val(expr)
4868            .map_err(|err| match err {
4869                proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
4870                    Error::ExpectedConstExprConcreteIntegerScalar(span)
4871                }
4872                proc::ConstValueError::Negative => Error::ExpectedNonNegative(span),
4873            })?;
4874        Ok((value, span))
4875    }
4876
4877    fn array_size(
4878        &mut self,
4879        expr: Handle<ast::Expression<'source>>,
4880        ctx: &mut ExpressionContext<'source, '_, '_>,
4881    ) -> Result<'source, ir::ArraySize> {
4882        let span = ctx.ast_expressions.get_span(expr);
4883        let const_ctx = &mut ctx.as_const();
4884        let const_expr = self.expression(expr, const_ctx);
4885        match const_expr {
4886            Ok(value) => {
4887                let len = const_ctx.get_const_val(value).map_err(|err| {
4888                    Box::new(match err {
4889                        proc::ConstValueError::NonConst | proc::ConstValueError::InvalidType => {
4890                            Error::ExpectedConstExprConcreteIntegerScalar(span)
4891                        }
4892                        proc::ConstValueError::Negative => Error::ExpectedPositiveArrayLength(span),
4893                    })
4894                })?;
4895                let size = NonZeroU32::new(len).ok_or(Error::ExpectedPositiveArrayLength(span))?;
4896                Ok(ir::ArraySize::Constant(size))
4897            }
4898            Err(err) => {
4899                // If the error is simply that `expr` was an override expression, then we
4900                // can represent that as an array length.
4901                let Error::ConstantEvaluatorError(ref ty, _) = *err else {
4902                    return Err(err);
4903                };
4904
4905                let proc::ConstantEvaluatorError::OverrideExpr = **ty else {
4906                    return Err(err);
4907                };
4908
4909                Ok(ir::ArraySize::Pending(self.array_size_override(
4910                    expr,
4911                    &mut ctx.as_global().as_override(),
4912                    span,
4913                )?))
4914            }
4915        }
4916    }
4917
4918    fn array_size_override(
4919        &mut self,
4920        size_expr: Handle<ast::Expression<'source>>,
4921        ctx: &mut ExpressionContext<'source, '_, '_>,
4922        span: Span,
4923    ) -> Result<'source, Handle<ir::Override>> {
4924        let expr = self.expression(size_expr, ctx)?;
4925        match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) {
4926            Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok({
4927                if let ir::Expression::Override(handle) = ctx.module.global_expressions[expr] {
4928                    handle
4929                } else {
4930                    let ty = ctx.register_type(expr)?;
4931                    ctx.module.overrides.append(
4932                        ir::Override {
4933                            name: None,
4934                            id: None,
4935                            ty,
4936                            init: Some(expr),
4937                        },
4938                        span,
4939                    )
4940                }
4941            }),
4942            _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar(
4943                span,
4944            ))),
4945        }
4946    }
4947
4948    /// Build the Naga equivalent of a named AST type.
4949    ///
4950    /// Return a Naga `Handle<Type>` representing the front-end type
4951    /// `handle`, which should be named `name`, if given.
4952    ///
4953    /// If `handle` refers to a type cached in [`SpecialTypes`],
4954    /// `name` may be ignored.
4955    ///
4956    /// [`SpecialTypes`]: ir::SpecialTypes
4957    fn resolve_named_ast_type(
4958        &mut self,
4959        ident: &ast::TemplateElaboratedIdent<'source>,
4960        name: String,
4961        ctx: &mut ExpressionContext<'source, '_, '_>,
4962    ) -> Result<'source, Handle<ir::Type>> {
4963        self.type_specifier(ident, ctx, Some(name))
4964    }
4965
4966    /// Return a Naga `Handle<Type>` representing the front-end type `handle`.
4967    fn resolve_ast_type(
4968        &mut self,
4969        ident: &ast::TemplateElaboratedIdent<'source>,
4970        ctx: &mut ExpressionContext<'source, '_, '_>,
4971    ) -> Result<'source, Handle<ir::Type>> {
4972        self.type_specifier(ident, ctx, None)
4973    }
4974
4975    fn binding(
4976        &mut self,
4977        binding: &Option<ast::Binding<'source>>,
4978        ty: Handle<ir::Type>,
4979        ctx: &mut GlobalContext<'source, '_, '_>,
4980    ) -> Result<'source, Option<ir::Binding>> {
4981        Ok(match *binding {
4982            Some(ast::Binding::BuiltIn(b)) => Some(ir::Binding::BuiltIn(b)),
4983            Some(ast::Binding::Location {
4984                location,
4985                interpolation,
4986                sampling,
4987                blend_src,
4988                per_primitive,
4989            }) => {
4990                let blend_src = if let Some(blend_src) = blend_src {
4991                    Some(self.const_u32(blend_src, &mut ctx.as_const())?.0)
4992                } else {
4993                    None
4994                };
4995
4996                let mut binding = ir::Binding::Location {
4997                    location: self.const_u32(location, &mut ctx.as_const())?.0,
4998                    interpolation,
4999                    sampling,
5000                    blend_src,
5001                    per_primitive,
5002                };
5003                binding.apply_default_interpolation(&ctx.module.types[ty].inner);
5004                Some(binding)
5005            }
5006            None => None,
5007        })
5008    }
5009
5010    fn ray_query_pointer(
5011        &mut self,
5012        expr: Handle<ast::Expression<'source>>,
5013        ctx: &mut ExpressionContext<'source, '_, '_>,
5014    ) -> Result<'source, Handle<ir::Expression>> {
5015        let span = ctx.ast_expressions.get_span(expr);
5016        let pointer = self.expression(expr, ctx)?;
5017
5018        match *resolve_inner!(ctx, pointer) {
5019            ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
5020                ir::TypeInner::RayQuery { .. } => Ok(pointer),
5021                ref other => {
5022                    log::error!("Pointer type to {other:?} passed to ray query op");
5023                    Err(Box::new(Error::InvalidRayQueryPointer(span)))
5024                }
5025            },
5026            ref other => {
5027                log::error!("Type {other:?} passed to ray query op");
5028                Err(Box::new(Error::InvalidRayQueryPointer(span)))
5029            }
5030        }
5031    }
5032}
5033
5034impl ir::AtomicFunction {
5035    pub fn map(word: &str) -> Option<Self> {
5036        Some(match word {
5037            "atomicAdd" => ir::AtomicFunction::Add,
5038            "atomicSub" => ir::AtomicFunction::Subtract,
5039            "atomicAnd" => ir::AtomicFunction::And,
5040            "atomicOr" => ir::AtomicFunction::InclusiveOr,
5041            "atomicXor" => ir::AtomicFunction::ExclusiveOr,
5042            "atomicMin" => ir::AtomicFunction::Min,
5043            "atomicMax" => ir::AtomicFunction::Max,
5044            "atomicExchange" => ir::AtomicFunction::Exchange { compare: None },
5045            _ => return None,
5046        })
5047    }
5048}