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