naga/front/wgsl/lower/
mod.rs

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