Skip to main content

naga/front/wgsl/lower/
mod.rs

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