Skip to main content

naga/front/wgsl/lower/
mod.rs

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