naga/front/wgsl/lower/
mod.rs

1use std::num::NonZeroU32;
2
3use crate::front::wgsl::error::{Error, ExpectedToken, InvalidAssignmentType};
4use crate::front::wgsl::index::Index;
5use crate::front::wgsl::parse::number::Number;
6use crate::front::wgsl::parse::{ast, conv};
7use crate::front::Typifier;
8use crate::proc::{
9    ensure_block_returns, Alignment, ConstantEvaluator, Emitter, Layouter, ResolveContext,
10};
11use crate::{Arena, FastHashMap, FastIndexMap, Handle, Span};
12
13mod construction;
14mod conversion;
15
16/// Resolves the inner type of a given expression.
17///
18/// Expects a &mut [`ExpressionContext`] and a [`Handle<Expression>`].
19///
20/// Returns a &[`crate::TypeInner`].
21///
22/// Ideally, we would simply have a function that takes a `&mut ExpressionContext`
23/// and returns a `&TypeResolution`. Unfortunately, this leads the borrow checker
24/// to conclude that the mutable borrow lasts for as long as we are using the
25/// `&TypeResolution`, so we can't use the `ExpressionContext` for anything else -
26/// like, say, resolving another operand's type. Using a macro that expands to
27/// two separate calls, only the first of which needs a `&mut`,
28/// lets the borrow checker see that the mutable borrow is over.
29macro_rules! resolve_inner {
30    ($ctx:ident, $expr:expr) => {{
31        $ctx.grow_types($expr)?;
32        $ctx.typifier()[$expr].inner_with(&$ctx.module.types)
33    }};
34}
35pub(super) use resolve_inner;
36
37/// Resolves the inner types of two given expressions.
38///
39/// Expects a &mut [`ExpressionContext`] and two [`Handle<Expression>`]s.
40///
41/// Returns a tuple containing two &[`crate::TypeInner`].
42///
43/// See the documentation of [`resolve_inner!`] for why this macro is necessary.
44macro_rules! resolve_inner_binary {
45    ($ctx:ident, $left:expr, $right:expr) => {{
46        $ctx.grow_types($left)?;
47        $ctx.grow_types($right)?;
48        (
49            $ctx.typifier()[$left].inner_with(&$ctx.module.types),
50            $ctx.typifier()[$right].inner_with(&$ctx.module.types),
51        )
52    }};
53}
54
55/// Resolves the type of a given expression.
56///
57/// Expects a &mut [`ExpressionContext`] and a [`Handle<Expression>`].
58///
59/// Returns a &[`TypeResolution`].
60///
61/// See the documentation of [`resolve_inner!`] for why this macro is necessary.
62///
63/// [`TypeResolution`]: crate::proc::TypeResolution
64macro_rules! resolve {
65    ($ctx:ident, $expr:expr) => {{
66        $ctx.grow_types($expr)?;
67        &$ctx.typifier()[$expr]
68    }};
69}
70pub(super) use resolve;
71
72/// State for constructing a `crate::Module`.
73pub struct GlobalContext<'source, 'temp, 'out> {
74    /// The `TranslationUnit`'s expressions arena.
75    ast_expressions: &'temp Arena<ast::Expression<'source>>,
76
77    /// The `TranslationUnit`'s types arena.
78    types: &'temp Arena<ast::Type<'source>>,
79
80    // Naga IR values.
81    /// The map from the names of module-scope declarations to the Naga IR
82    /// `Handle`s we have built for them, owned by `Lowerer::lower`.
83    globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,
84
85    /// The module we're constructing.
86    module: &'out mut crate::Module,
87
88    const_typifier: &'temp mut Typifier,
89
90    global_expression_kind_tracker: &'temp mut crate::proc::ExpressionKindTracker,
91}
92
93impl<'source> GlobalContext<'source, '_, '_> {
94    fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> {
95        ExpressionContext {
96            ast_expressions: self.ast_expressions,
97            globals: self.globals,
98            types: self.types,
99            module: self.module,
100            const_typifier: self.const_typifier,
101            expr_type: ExpressionContextType::Constant,
102            global_expression_kind_tracker: self.global_expression_kind_tracker,
103        }
104    }
105
106    fn as_override(&mut self) -> ExpressionContext<'source, '_, '_> {
107        ExpressionContext {
108            ast_expressions: self.ast_expressions,
109            globals: self.globals,
110            types: self.types,
111            module: self.module,
112            const_typifier: self.const_typifier,
113            expr_type: ExpressionContextType::Override,
114            global_expression_kind_tracker: self.global_expression_kind_tracker,
115        }
116    }
117
118    fn ensure_type_exists(
119        &mut self,
120        name: Option<String>,
121        inner: crate::TypeInner,
122    ) -> Handle<crate::Type> {
123        self.module
124            .types
125            .insert(crate::Type { inner, name }, Span::UNDEFINED)
126    }
127}
128
129/// State for lowering a statement within a function.
130pub struct StatementContext<'source, 'temp, 'out> {
131    // WGSL AST values.
132    /// A reference to [`TranslationUnit::expressions`] for the translation unit
133    /// we're lowering.
134    ///
135    /// [`TranslationUnit::expressions`]: ast::TranslationUnit::expressions
136    ast_expressions: &'temp Arena<ast::Expression<'source>>,
137
138    /// A reference to [`TranslationUnit::types`] for the translation unit
139    /// we're lowering.
140    ///
141    /// [`TranslationUnit::types`]: ast::TranslationUnit::types
142    types: &'temp Arena<ast::Type<'source>>,
143
144    // Naga IR values.
145    /// The map from the names of module-scope declarations to the Naga IR
146    /// `Handle`s we have built for them, owned by `Lowerer::lower`.
147    globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,
148
149    /// A map from each `ast::Local` handle to the Naga expression
150    /// we've built for it:
151    ///
152    /// - WGSL function arguments become Naga [`FunctionArgument`] expressions.
153    ///
154    /// - WGSL `var` declarations become Naga [`LocalVariable`] expressions.
155    ///
156    /// - WGSL `let` declararations become arbitrary Naga expressions.
157    ///
158    /// This always borrows the `local_table` local variable in
159    /// [`Lowerer::function`].
160    ///
161    /// [`LocalVariable`]: crate::Expression::LocalVariable
162    /// [`FunctionArgument`]: crate::Expression::FunctionArgument
163    local_table: &'temp mut FastHashMap<Handle<ast::Local>, Typed<Handle<crate::Expression>>>,
164
165    const_typifier: &'temp mut Typifier,
166    typifier: &'temp mut Typifier,
167    function: &'out mut crate::Function,
168    /// Stores the names of expressions that are assigned in `let` statement
169    /// Also stores the spans of the names, for use in errors.
170    named_expressions: &'out mut FastIndexMap<Handle<crate::Expression>, (String, Span)>,
171    module: &'out mut crate::Module,
172
173    /// Which `Expression`s in `self.naga_expressions` are const expressions, in
174    /// the WGSL sense.
175    ///
176    /// According to the WGSL spec, a const expression must not refer to any
177    /// `let` declarations, even if those declarations' initializers are
178    /// themselves const expressions. So this tracker is not simply concerned
179    /// with the form of the expressions; it is also tracking whether WGSL says
180    /// we should consider them to be const. See the use of `force_non_const` in
181    /// the code for lowering `let` bindings.
182    local_expression_kind_tracker: &'temp mut crate::proc::ExpressionKindTracker,
183    global_expression_kind_tracker: &'temp mut crate::proc::ExpressionKindTracker,
184}
185
186impl<'a, 'temp> StatementContext<'a, 'temp, '_> {
187    fn as_expression<'t>(
188        &'t mut self,
189        block: &'t mut crate::Block,
190        emitter: &'t mut Emitter,
191    ) -> ExpressionContext<'a, 't, '_>
192    where
193        'temp: 't,
194    {
195        ExpressionContext {
196            globals: self.globals,
197            types: self.types,
198            ast_expressions: self.ast_expressions,
199            const_typifier: self.const_typifier,
200            global_expression_kind_tracker: self.global_expression_kind_tracker,
201            module: self.module,
202            expr_type: ExpressionContextType::Runtime(RuntimeExpressionContext {
203                local_table: self.local_table,
204                function: self.function,
205                block,
206                emitter,
207                typifier: self.typifier,
208                local_expression_kind_tracker: self.local_expression_kind_tracker,
209            }),
210        }
211    }
212
213    fn as_global(&mut self) -> GlobalContext<'a, '_, '_> {
214        GlobalContext {
215            ast_expressions: self.ast_expressions,
216            globals: self.globals,
217            types: self.types,
218            module: self.module,
219            const_typifier: self.const_typifier,
220            global_expression_kind_tracker: self.global_expression_kind_tracker,
221        }
222    }
223
224    fn invalid_assignment_type(&self, expr: Handle<crate::Expression>) -> InvalidAssignmentType {
225        if let Some(&(_, span)) = self.named_expressions.get(&expr) {
226            InvalidAssignmentType::ImmutableBinding(span)
227        } else {
228            match self.function.expressions[expr] {
229                crate::Expression::Swizzle { .. } => InvalidAssignmentType::Swizzle,
230                crate::Expression::Access { base, .. } => self.invalid_assignment_type(base),
231                crate::Expression::AccessIndex { base, .. } => self.invalid_assignment_type(base),
232                _ => InvalidAssignmentType::Other,
233            }
234        }
235    }
236}
237
238pub struct RuntimeExpressionContext<'temp, 'out> {
239    /// A map from [`ast::Local`] handles to the Naga expressions we've built for them.
240    ///
241    /// This is always [`StatementContext::local_table`] for the
242    /// enclosing statement; see that documentation for details.
243    local_table: &'temp FastHashMap<Handle<ast::Local>, Typed<Handle<crate::Expression>>>,
244
245    function: &'out mut crate::Function,
246    block: &'temp mut crate::Block,
247    emitter: &'temp mut Emitter,
248    typifier: &'temp mut Typifier,
249
250    /// Which `Expression`s in `self.naga_expressions` are const expressions, in
251    /// the WGSL sense.
252    ///
253    /// See [`StatementContext::local_expression_kind_tracker`] for details.
254    local_expression_kind_tracker: &'temp mut crate::proc::ExpressionKindTracker,
255}
256
257/// The type of Naga IR expression we are lowering an [`ast::Expression`] to.
258pub enum ExpressionContextType<'temp, 'out> {
259    /// We are lowering to an arbitrary runtime expression, to be
260    /// included in a function's body.
261    ///
262    /// The given [`RuntimeExpressionContext`] holds information about local
263    /// variables, arguments, and other definitions available only to runtime
264    /// expressions, not constant or override expressions.
265    Runtime(RuntimeExpressionContext<'temp, 'out>),
266
267    /// We are lowering to a constant expression, to be included in the module's
268    /// constant expression arena.
269    ///
270    /// Everything constant expressions are allowed to refer to is
271    /// available in the [`ExpressionContext`], so this variant
272    /// carries no further information.
273    Constant,
274
275    /// We are lowering to an override expression, to be included in the module's
276    /// constant expression arena.
277    ///
278    /// Everything override expressions are allowed to refer to is
279    /// available in the [`ExpressionContext`], so this variant
280    /// carries no further information.
281    Override,
282}
283
284/// State for lowering an [`ast::Expression`] to Naga IR.
285///
286/// [`ExpressionContext`]s come in two kinds, distinguished by
287/// the value of the [`expr_type`] field:
288///
289/// - A [`Runtime`] context contributes [`naga::Expression`]s to a [`naga::Function`]'s
290///   runtime expression arena.
291///
292/// - A [`Constant`] context contributes [`naga::Expression`]s to a [`naga::Module`]'s
293///   constant expression arena.
294///
295/// [`ExpressionContext`]s are constructed in restricted ways:
296///
297/// - To get a [`Runtime`] [`ExpressionContext`], call
298///   [`StatementContext::as_expression`].
299///
300/// - To get a [`Constant`] [`ExpressionContext`], call
301///   [`GlobalContext::as_const`].
302///
303/// - You can demote a [`Runtime`] context to a [`Constant`] context
304///   by calling [`as_const`], but there's no way to go in the other
305///   direction, producing a runtime context from a constant one. This
306///   is because runtime expressions can refer to constant
307///   expressions, via [`Expression::Constant`], but constant
308///   expressions can't refer to a function's expressions.
309///
310/// Not to be confused with `wgsl::parse::ExpressionContext`, which is
311/// for parsing the `ast::Expression` in the first place.
312///
313/// [`expr_type`]: ExpressionContext::expr_type
314/// [`Runtime`]: ExpressionContextType::Runtime
315/// [`naga::Expression`]: crate::Expression
316/// [`naga::Function`]: crate::Function
317/// [`Constant`]: ExpressionContextType::Constant
318/// [`naga::Module`]: crate::Module
319/// [`as_const`]: ExpressionContext::as_const
320/// [`Expression::Constant`]: crate::Expression::Constant
321pub struct ExpressionContext<'source, 'temp, 'out> {
322    // WGSL AST values.
323    ast_expressions: &'temp Arena<ast::Expression<'source>>,
324    types: &'temp Arena<ast::Type<'source>>,
325
326    // Naga IR values.
327    /// The map from the names of module-scope declarations to the Naga IR
328    /// `Handle`s we have built for them, owned by `Lowerer::lower`.
329    globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>,
330
331    /// The IR [`Module`] we're constructing.
332    ///
333    /// [`Module`]: crate::Module
334    module: &'out mut crate::Module,
335
336    /// Type judgments for [`module::global_expressions`].
337    ///
338    /// [`module::global_expressions`]: crate::Module::global_expressions
339    const_typifier: &'temp mut Typifier,
340    global_expression_kind_tracker: &'temp mut crate::proc::ExpressionKindTracker,
341
342    /// Whether we are lowering a constant expression or a general
343    /// runtime expression, and the data needed in each case.
344    expr_type: ExpressionContextType<'temp, 'out>,
345}
346
347impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> {
348    fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> {
349        ExpressionContext {
350            globals: self.globals,
351            types: self.types,
352            ast_expressions: self.ast_expressions,
353            const_typifier: self.const_typifier,
354            module: self.module,
355            expr_type: ExpressionContextType::Constant,
356            global_expression_kind_tracker: self.global_expression_kind_tracker,
357        }
358    }
359
360    fn as_global(&mut self) -> GlobalContext<'source, '_, '_> {
361        GlobalContext {
362            ast_expressions: self.ast_expressions,
363            globals: self.globals,
364            types: self.types,
365            module: self.module,
366            const_typifier: self.const_typifier,
367            global_expression_kind_tracker: self.global_expression_kind_tracker,
368        }
369    }
370
371    fn as_const_evaluator(&mut self) -> ConstantEvaluator {
372        match self.expr_type {
373            ExpressionContextType::Runtime(ref mut rctx) => ConstantEvaluator::for_wgsl_function(
374                self.module,
375                &mut rctx.function.expressions,
376                rctx.local_expression_kind_tracker,
377                rctx.emitter,
378                rctx.block,
379            ),
380            ExpressionContextType::Constant => ConstantEvaluator::for_wgsl_module(
381                self.module,
382                self.global_expression_kind_tracker,
383                false,
384            ),
385            ExpressionContextType::Override => ConstantEvaluator::for_wgsl_module(
386                self.module,
387                self.global_expression_kind_tracker,
388                true,
389            ),
390        }
391    }
392
393    fn append_expression(
394        &mut self,
395        expr: crate::Expression,
396        span: Span,
397    ) -> Result<Handle<crate::Expression>, Error<'source>> {
398        let mut eval = self.as_const_evaluator();
399        eval.try_eval_and_append(expr, span)
400            .map_err(|e| Error::ConstantEvaluatorError(e, span))
401    }
402
403    fn const_access(&self, handle: Handle<crate::Expression>) -> Option<u32> {
404        match self.expr_type {
405            ExpressionContextType::Runtime(ref ctx) => {
406                if !ctx.local_expression_kind_tracker.is_const(handle) {
407                    return None;
408                }
409
410                self.module
411                    .to_ctx()
412                    .eval_expr_to_u32_from(handle, &ctx.function.expressions)
413                    .ok()
414            }
415            ExpressionContextType::Constant => self.module.to_ctx().eval_expr_to_u32(handle).ok(),
416            ExpressionContextType::Override => None,
417        }
418    }
419
420    fn get_expression_span(&self, handle: Handle<crate::Expression>) -> Span {
421        match self.expr_type {
422            ExpressionContextType::Runtime(ref ctx) => ctx.function.expressions.get_span(handle),
423            ExpressionContextType::Constant | ExpressionContextType::Override => {
424                self.module.global_expressions.get_span(handle)
425            }
426        }
427    }
428
429    fn typifier(&self) -> &Typifier {
430        match self.expr_type {
431            ExpressionContextType::Runtime(ref ctx) => ctx.typifier,
432            ExpressionContextType::Constant | ExpressionContextType::Override => {
433                self.const_typifier
434            }
435        }
436    }
437
438    fn runtime_expression_ctx(
439        &mut self,
440        span: Span,
441    ) -> Result<&mut RuntimeExpressionContext<'temp, 'out>, Error<'source>> {
442        match self.expr_type {
443            ExpressionContextType::Runtime(ref mut ctx) => Ok(ctx),
444            ExpressionContextType::Constant | ExpressionContextType::Override => {
445                Err(Error::UnexpectedOperationInConstContext(span))
446            }
447        }
448    }
449
450    fn gather_component(
451        &mut self,
452        expr: Handle<crate::Expression>,
453        component_span: Span,
454        gather_span: Span,
455    ) -> Result<crate::SwizzleComponent, Error<'source>> {
456        match self.expr_type {
457            ExpressionContextType::Runtime(ref rctx) => {
458                if !rctx.local_expression_kind_tracker.is_const(expr) {
459                    return Err(Error::ExpectedConstExprConcreteIntegerScalar(
460                        component_span,
461                    ));
462                }
463
464                let index = self
465                    .module
466                    .to_ctx()
467                    .eval_expr_to_u32_from(expr, &rctx.function.expressions)
468                    .map_err(|err| match err {
469                        crate::proc::U32EvalError::NonConst => {
470                            Error::ExpectedConstExprConcreteIntegerScalar(component_span)
471                        }
472                        crate::proc::U32EvalError::Negative => {
473                            Error::ExpectedNonNegative(component_span)
474                        }
475                    })?;
476                crate::SwizzleComponent::XYZW
477                    .get(index as usize)
478                    .copied()
479                    .ok_or(Error::InvalidGatherComponent(component_span))
480            }
481            // This means a `gather` operation appeared in a constant expression.
482            // This error refers to the `gather` itself, not its "component" argument.
483            ExpressionContextType::Constant | ExpressionContextType::Override => {
484                Err(Error::UnexpectedOperationInConstContext(gather_span))
485            }
486        }
487    }
488
489    /// Determine the type of `handle`, and add it to the module's arena.
490    ///
491    /// If you just need a `TypeInner` for `handle`'s type, use the
492    /// [`resolve_inner!`] macro instead. This function
493    /// should only be used when the type of `handle` needs to appear
494    /// in the module's final `Arena<Type>`, for example, if you're
495    /// creating a [`LocalVariable`] whose type is inferred from its
496    /// initializer.
497    ///
498    /// [`LocalVariable`]: crate::LocalVariable
499    fn register_type(
500        &mut self,
501        handle: Handle<crate::Expression>,
502    ) -> Result<Handle<crate::Type>, Error<'source>> {
503        self.grow_types(handle)?;
504        // This is equivalent to calling ExpressionContext::typifier(),
505        // except that this lets the borrow checker see that it's okay
506        // to also borrow self.module.types mutably below.
507        let typifier = match self.expr_type {
508            ExpressionContextType::Runtime(ref ctx) => ctx.typifier,
509            ExpressionContextType::Constant | ExpressionContextType::Override => {
510                &*self.const_typifier
511            }
512        };
513        Ok(typifier.register_type(handle, &mut self.module.types))
514    }
515
516    /// Resolve the types of all expressions up through `handle`.
517    ///
518    /// Ensure that [`self.typifier`] has a [`TypeResolution`] for
519    /// every expression in [`self.function.expressions`].
520    ///
521    /// This does not add types to any arena. The [`Typifier`]
522    /// documentation explains the steps we take to avoid filling
523    /// arenas with intermediate types.
524    ///
525    /// This function takes `&mut self`, so it can't conveniently
526    /// return a shared reference to the resulting `TypeResolution`:
527    /// the shared reference would extend the mutable borrow, and you
528    /// wouldn't be able to use `self` for anything else. Instead, you
529    /// should use [`register_type`] or one of [`resolve!`],
530    /// [`resolve_inner!`] or [`resolve_inner_binary!`].
531    ///
532    /// [`self.typifier`]: ExpressionContext::typifier
533    /// [`TypeResolution`]: crate::proc::TypeResolution
534    /// [`register_type`]: Self::register_type
535    /// [`Typifier`]: Typifier
536    fn grow_types(
537        &mut self,
538        handle: Handle<crate::Expression>,
539    ) -> Result<&mut Self, Error<'source>> {
540        let empty_arena = Arena::new();
541        let resolve_ctx;
542        let typifier;
543        let expressions;
544        match self.expr_type {
545            ExpressionContextType::Runtime(ref mut ctx) => {
546                resolve_ctx = ResolveContext::with_locals(
547                    self.module,
548                    &ctx.function.local_variables,
549                    &ctx.function.arguments,
550                );
551                typifier = &mut *ctx.typifier;
552                expressions = &ctx.function.expressions;
553            }
554            ExpressionContextType::Constant | ExpressionContextType::Override => {
555                resolve_ctx = ResolveContext::with_locals(self.module, &empty_arena, &[]);
556                typifier = self.const_typifier;
557                expressions = &self.module.global_expressions;
558            }
559        };
560        typifier
561            .grow(handle, expressions, &resolve_ctx)
562            .map_err(Error::InvalidResolve)?;
563
564        Ok(self)
565    }
566
567    fn image_data(
568        &mut self,
569        image: Handle<crate::Expression>,
570        span: Span,
571    ) -> Result<(crate::ImageClass, bool), Error<'source>> {
572        match *resolve_inner!(self, image) {
573            crate::TypeInner::Image { class, arrayed, .. } => Ok((class, arrayed)),
574            _ => Err(Error::BadTexture(span)),
575        }
576    }
577
578    fn prepare_args<'b>(
579        &mut self,
580        args: &'b [Handle<ast::Expression<'source>>],
581        min_args: u32,
582        span: Span,
583    ) -> ArgumentContext<'b, 'source> {
584        ArgumentContext {
585            args: args.iter(),
586            min_args,
587            args_used: 0,
588            total_args: args.len() as u32,
589            span,
590        }
591    }
592
593    /// Insert splats, if needed by the non-'*' operations.
594    ///
595    /// See the "Binary arithmetic expressions with mixed scalar and vector operands"
596    /// table in the WebGPU Shading Language specification for relevant operators.
597    ///
598    /// Multiply is not handled here as backends are expected to handle vec*scalar
599    /// operations, so inserting splats into the IR increases size needlessly.
600    fn binary_op_splat(
601        &mut self,
602        op: crate::BinaryOperator,
603        left: &mut Handle<crate::Expression>,
604        right: &mut Handle<crate::Expression>,
605    ) -> Result<(), Error<'source>> {
606        if matches!(
607            op,
608            crate::BinaryOperator::Add
609                | crate::BinaryOperator::Subtract
610                | crate::BinaryOperator::Divide
611                | crate::BinaryOperator::Modulo
612        ) {
613            match resolve_inner_binary!(self, *left, *right) {
614                (&crate::TypeInner::Vector { size, .. }, &crate::TypeInner::Scalar { .. }) => {
615                    *right = self.append_expression(
616                        crate::Expression::Splat {
617                            size,
618                            value: *right,
619                        },
620                        self.get_expression_span(*right),
621                    )?;
622                }
623                (&crate::TypeInner::Scalar { .. }, &crate::TypeInner::Vector { size, .. }) => {
624                    *left = self.append_expression(
625                        crate::Expression::Splat { size, value: *left },
626                        self.get_expression_span(*left),
627                    )?;
628                }
629                _ => {}
630            }
631        }
632
633        Ok(())
634    }
635
636    /// Add a single expression to the expression table that is not covered by `self.emitter`.
637    ///
638    /// This is useful for `CallResult` and `AtomicResult` expressions, which should not be covered by
639    /// `Emit` statements.
640    fn interrupt_emitter(
641        &mut self,
642        expression: crate::Expression,
643        span: Span,
644    ) -> Result<Handle<crate::Expression>, Error<'source>> {
645        match self.expr_type {
646            ExpressionContextType::Runtime(ref mut rctx) => {
647                rctx.block
648                    .extend(rctx.emitter.finish(&rctx.function.expressions));
649            }
650            ExpressionContextType::Constant | ExpressionContextType::Override => {}
651        }
652        let result = self.append_expression(expression, span);
653        match self.expr_type {
654            ExpressionContextType::Runtime(ref mut rctx) => {
655                rctx.emitter.start(&rctx.function.expressions);
656            }
657            ExpressionContextType::Constant | ExpressionContextType::Override => {}
658        }
659        result
660    }
661
662    /// Apply the WGSL Load Rule to `expr`.
663    ///
664    /// If `expr` is has type `ref<SC, T, A>`, perform a load to produce a value of type
665    /// `T`. Otherwise, return `expr` unchanged.
666    fn apply_load_rule(
667        &mut self,
668        expr: Typed<Handle<crate::Expression>>,
669    ) -> Result<Handle<crate::Expression>, Error<'source>> {
670        match expr {
671            Typed::Reference(pointer) => {
672                let load = crate::Expression::Load { pointer };
673                let span = self.get_expression_span(pointer);
674                self.append_expression(load, span)
675            }
676            Typed::Plain(handle) => Ok(handle),
677        }
678    }
679
680    fn ensure_type_exists(&mut self, inner: crate::TypeInner) -> Handle<crate::Type> {
681        self.as_global().ensure_type_exists(None, inner)
682    }
683}
684
685struct ArgumentContext<'ctx, 'source> {
686    args: std::slice::Iter<'ctx, Handle<ast::Expression<'source>>>,
687    min_args: u32,
688    args_used: u32,
689    total_args: u32,
690    span: Span,
691}
692
693impl<'source> ArgumentContext<'_, 'source> {
694    pub fn finish(self) -> Result<(), Error<'source>> {
695        if self.args.len() == 0 {
696            Ok(())
697        } else {
698            Err(Error::WrongArgumentCount {
699                found: self.total_args,
700                expected: self.min_args..self.args_used + 1,
701                span: self.span,
702            })
703        }
704    }
705
706    pub fn next(&mut self) -> Result<Handle<ast::Expression<'source>>, Error<'source>> {
707        match self.args.next().copied() {
708            Some(arg) => {
709                self.args_used += 1;
710                Ok(arg)
711            }
712            None => Err(Error::WrongArgumentCount {
713                found: self.total_args,
714                expected: self.min_args..self.args_used + 1,
715                span: self.span,
716            }),
717        }
718    }
719}
720
721/// WGSL type annotations on expressions, types, values, etc.
722///
723/// Naga and WGSL types are very close, but Naga lacks WGSL's `ref` types, which
724/// we need to know to apply the Load Rule. This enum carries some WGSL or Naga
725/// datum along with enough information to determine its corresponding WGSL
726/// type.
727///
728/// The `T` type parameter can be any expression-like thing:
729///
730/// - `Typed<Handle<crate::Type>>` can represent a full WGSL type. For example,
731///   given some Naga `Pointer` type `ptr`, a WGSL reference type is a
732///   `Typed::Reference(ptr)` whereas a WGSL pointer type is a
733///   `Typed::Plain(ptr)`.
734///
735/// - `Typed<crate::Expression>` or `Typed<Handle<crate::Expression>>` can
736///   represent references similarly.
737///
738/// Use the `map` and `try_map` methods to convert from one expression
739/// representation to another.
740///
741/// [`Expression`]: crate::Expression
742#[derive(Debug, Copy, Clone)]
743enum Typed<T> {
744    /// A WGSL reference.
745    Reference(T),
746
747    /// A WGSL plain type.
748    Plain(T),
749}
750
751impl<T> Typed<T> {
752    fn map<U>(self, mut f: impl FnMut(T) -> U) -> Typed<U> {
753        match self {
754            Self::Reference(v) => Typed::Reference(f(v)),
755            Self::Plain(v) => Typed::Plain(f(v)),
756        }
757    }
758
759    fn try_map<U, E>(self, mut f: impl FnMut(T) -> Result<U, E>) -> Result<Typed<U>, E> {
760        Ok(match self {
761            Self::Reference(expr) => Typed::Reference(f(expr)?),
762            Self::Plain(expr) => Typed::Plain(f(expr)?),
763        })
764    }
765}
766
767/// A single vector component or swizzle.
768///
769/// This represents the things that can appear after the `.` in a vector access
770/// expression: either a single component name, or a series of them,
771/// representing a swizzle.
772enum Components {
773    Single(u32),
774    Swizzle {
775        size: crate::VectorSize,
776        pattern: [crate::SwizzleComponent; 4],
777    },
778}
779
780impl Components {
781    const fn letter_component(letter: char) -> Option<crate::SwizzleComponent> {
782        use crate::SwizzleComponent as Sc;
783        match letter {
784            'x' | 'r' => Some(Sc::X),
785            'y' | 'g' => Some(Sc::Y),
786            'z' | 'b' => Some(Sc::Z),
787            'w' | 'a' => Some(Sc::W),
788            _ => None,
789        }
790    }
791
792    fn single_component(name: &str, name_span: Span) -> Result<u32, Error> {
793        let ch = name.chars().next().ok_or(Error::BadAccessor(name_span))?;
794        match Self::letter_component(ch) {
795            Some(sc) => Ok(sc as u32),
796            None => Err(Error::BadAccessor(name_span)),
797        }
798    }
799
800    /// Construct a `Components` value from a 'member' name, like `"wzy"` or `"x"`.
801    ///
802    /// Use `name_span` for reporting errors in parsing the component string.
803    fn new(name: &str, name_span: Span) -> Result<Self, Error> {
804        let size = match name.len() {
805            1 => return Ok(Components::Single(Self::single_component(name, name_span)?)),
806            2 => crate::VectorSize::Bi,
807            3 => crate::VectorSize::Tri,
808            4 => crate::VectorSize::Quad,
809            _ => return Err(Error::BadAccessor(name_span)),
810        };
811
812        let mut pattern = [crate::SwizzleComponent::X; 4];
813        for (comp, ch) in pattern.iter_mut().zip(name.chars()) {
814            *comp = Self::letter_component(ch).ok_or(Error::BadAccessor(name_span))?;
815        }
816
817        Ok(Components::Swizzle { size, pattern })
818    }
819}
820
821/// An `ast::GlobalDecl` for which we have built the Naga IR equivalent.
822enum LoweredGlobalDecl {
823    Function(Handle<crate::Function>),
824    Var(Handle<crate::GlobalVariable>),
825    Const(Handle<crate::Constant>),
826    Override(Handle<crate::Override>),
827    Type(Handle<crate::Type>),
828    EntryPoint,
829}
830
831enum Texture {
832    Gather,
833    GatherCompare,
834
835    Sample,
836    SampleBias,
837    SampleCompare,
838    SampleCompareLevel,
839    SampleGrad,
840    SampleLevel,
841    // SampleBaseClampToEdge,
842}
843
844impl Texture {
845    pub fn map(word: &str) -> Option<Self> {
846        Some(match word {
847            "textureGather" => Self::Gather,
848            "textureGatherCompare" => Self::GatherCompare,
849
850            "textureSample" => Self::Sample,
851            "textureSampleBias" => Self::SampleBias,
852            "textureSampleCompare" => Self::SampleCompare,
853            "textureSampleCompareLevel" => Self::SampleCompareLevel,
854            "textureSampleGrad" => Self::SampleGrad,
855            "textureSampleLevel" => Self::SampleLevel,
856            // "textureSampleBaseClampToEdge" => Some(Self::SampleBaseClampToEdge),
857            _ => return None,
858        })
859    }
860
861    pub const fn min_argument_count(&self) -> u32 {
862        match *self {
863            Self::Gather => 3,
864            Self::GatherCompare => 4,
865
866            Self::Sample => 3,
867            Self::SampleBias => 5,
868            Self::SampleCompare => 5,
869            Self::SampleCompareLevel => 5,
870            Self::SampleGrad => 6,
871            Self::SampleLevel => 5,
872            // Self::SampleBaseClampToEdge => 3,
873        }
874    }
875}
876
877enum SubgroupGather {
878    BroadcastFirst,
879    Broadcast,
880    Shuffle,
881    ShuffleDown,
882    ShuffleUp,
883    ShuffleXor,
884}
885
886impl SubgroupGather {
887    pub fn map(word: &str) -> Option<Self> {
888        Some(match word {
889            "subgroupBroadcastFirst" => Self::BroadcastFirst,
890            "subgroupBroadcast" => Self::Broadcast,
891            "subgroupShuffle" => Self::Shuffle,
892            "subgroupShuffleDown" => Self::ShuffleDown,
893            "subgroupShuffleUp" => Self::ShuffleUp,
894            "subgroupShuffleXor" => Self::ShuffleXor,
895            _ => return None,
896        })
897    }
898}
899
900pub struct Lowerer<'source, 'temp> {
901    index: &'temp Index<'source>,
902    layouter: Layouter,
903}
904
905impl<'source, 'temp> Lowerer<'source, 'temp> {
906    pub fn new(index: &'temp Index<'source>) -> Self {
907        Self {
908            index,
909            layouter: Layouter::default(),
910        }
911    }
912
913    pub fn lower(
914        &mut self,
915        tu: &'temp ast::TranslationUnit<'source>,
916    ) -> Result<crate::Module, Error<'source>> {
917        let mut module = crate::Module::default();
918
919        let mut ctx = GlobalContext {
920            ast_expressions: &tu.expressions,
921            globals: &mut FastHashMap::default(),
922            types: &tu.types,
923            module: &mut module,
924            const_typifier: &mut Typifier::new(),
925            global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker::new(),
926        };
927
928        for decl_handle in self.index.visit_ordered() {
929            let span = tu.decls.get_span(decl_handle);
930            let decl = &tu.decls[decl_handle];
931
932            match decl.kind {
933                ast::GlobalDeclKind::Fn(ref f) => {
934                    let lowered_decl = self.function(f, span, &mut ctx)?;
935                    ctx.globals.insert(f.name.name, lowered_decl);
936                }
937                ast::GlobalDeclKind::Var(ref v) => {
938                    let ty = self.resolve_ast_type(v.ty, &mut ctx)?;
939
940                    let init;
941                    if let Some(init_ast) = v.init {
942                        let mut ectx = ctx.as_override();
943                        let lowered = self.expression_for_abstract(init_ast, &mut ectx)?;
944                        let ty_res = crate::proc::TypeResolution::Handle(ty);
945                        let converted = ectx
946                            .try_automatic_conversions(lowered, &ty_res, v.name.span)
947                            .map_err(|error| match error {
948                                Error::AutoConversion {
949                                    dest_span: _,
950                                    dest_type,
951                                    source_span: _,
952                                    source_type,
953                                } => Error::InitializationTypeMismatch {
954                                    name: v.name.span,
955                                    expected: dest_type,
956                                    got: source_type,
957                                },
958                                other => other,
959                            })?;
960                        init = Some(converted);
961                    } else {
962                        init = None;
963                    }
964
965                    let binding = if let Some(ref binding) = v.binding {
966                        Some(crate::ResourceBinding {
967                            group: self.const_u32(binding.group, &mut ctx.as_const())?.0,
968                            binding: self.const_u32(binding.binding, &mut ctx.as_const())?.0,
969                        })
970                    } else {
971                        None
972                    };
973
974                    let handle = ctx.module.global_variables.append(
975                        crate::GlobalVariable {
976                            name: Some(v.name.name.to_string()),
977                            space: v.space,
978                            binding,
979                            ty,
980                            init,
981                        },
982                        span,
983                    );
984
985                    ctx.globals
986                        .insert(v.name.name, LoweredGlobalDecl::Var(handle));
987                }
988                ast::GlobalDeclKind::Const(ref c) => {
989                    let mut ectx = ctx.as_const();
990                    let mut init = self.expression_for_abstract(c.init, &mut ectx)?;
991
992                    let ty;
993                    if let Some(explicit_ty) = c.ty {
994                        let explicit_ty =
995                            self.resolve_ast_type(explicit_ty, &mut ectx.as_global())?;
996                        let explicit_ty_res = crate::proc::TypeResolution::Handle(explicit_ty);
997                        init = ectx
998                            .try_automatic_conversions(init, &explicit_ty_res, c.name.span)
999                            .map_err(|error| match error {
1000                                Error::AutoConversion {
1001                                    dest_span: _,
1002                                    dest_type,
1003                                    source_span: _,
1004                                    source_type,
1005                                } => Error::InitializationTypeMismatch {
1006                                    name: c.name.span,
1007                                    expected: dest_type,
1008                                    got: source_type,
1009                                },
1010                                other => other,
1011                            })?;
1012                        ty = explicit_ty;
1013                    } else {
1014                        init = ectx.concretize(init)?;
1015                        ty = ectx.register_type(init)?;
1016                    }
1017
1018                    let handle = ctx.module.constants.append(
1019                        crate::Constant {
1020                            name: Some(c.name.name.to_string()),
1021                            ty,
1022                            init,
1023                        },
1024                        span,
1025                    );
1026
1027                    ctx.globals
1028                        .insert(c.name.name, LoweredGlobalDecl::Const(handle));
1029                }
1030                ast::GlobalDeclKind::Override(ref o) => {
1031                    let init = o
1032                        .init
1033                        .map(|init| self.expression(init, &mut ctx.as_override()))
1034                        .transpose()?;
1035                    let inferred_type = init
1036                        .map(|init| ctx.as_const().register_type(init))
1037                        .transpose()?;
1038
1039                    let explicit_ty =
1040                        o.ty.map(|ty| self.resolve_ast_type(ty, &mut ctx))
1041                            .transpose()?;
1042
1043                    let id =
1044                        o.id.map(|id| self.const_u32(id, &mut ctx.as_const()))
1045                            .transpose()?;
1046
1047                    let id = if let Some((id, id_span)) = id {
1048                        Some(
1049                            u16::try_from(id)
1050                                .map_err(|_| Error::PipelineConstantIDValue(id_span))?,
1051                        )
1052                    } else {
1053                        None
1054                    };
1055
1056                    let ty = match (explicit_ty, inferred_type) {
1057                        (Some(explicit_ty), Some(inferred_type)) => {
1058                            if explicit_ty == inferred_type {
1059                                explicit_ty
1060                            } else {
1061                                let gctx = ctx.module.to_ctx();
1062                                return Err(Error::InitializationTypeMismatch {
1063                                    name: o.name.span,
1064                                    expected: explicit_ty.to_wgsl(&gctx),
1065                                    got: inferred_type.to_wgsl(&gctx),
1066                                });
1067                            }
1068                        }
1069                        (Some(explicit_ty), None) => explicit_ty,
1070                        (None, Some(inferred_type)) => inferred_type,
1071                        (None, None) => {
1072                            return Err(Error::DeclMissingTypeAndInit(o.name.span));
1073                        }
1074                    };
1075
1076                    let handle = ctx.module.overrides.append(
1077                        crate::Override {
1078                            name: Some(o.name.name.to_string()),
1079                            id,
1080                            ty,
1081                            init,
1082                        },
1083                        span,
1084                    );
1085
1086                    ctx.globals
1087                        .insert(o.name.name, LoweredGlobalDecl::Override(handle));
1088                }
1089                ast::GlobalDeclKind::Struct(ref s) => {
1090                    let handle = self.r#struct(s, span, &mut ctx)?;
1091                    ctx.globals
1092                        .insert(s.name.name, LoweredGlobalDecl::Type(handle));
1093                }
1094                ast::GlobalDeclKind::Type(ref alias) => {
1095                    let ty = self.resolve_named_ast_type(
1096                        alias.ty,
1097                        Some(alias.name.name.to_string()),
1098                        &mut ctx,
1099                    )?;
1100                    ctx.globals
1101                        .insert(alias.name.name, LoweredGlobalDecl::Type(ty));
1102                }
1103            }
1104        }
1105
1106        // Constant evaluation may leave abstract-typed literals and
1107        // compositions in expression arenas, so we need to compact the module
1108        // to remove unused expressions and types.
1109        crate::compact::compact(&mut module);
1110
1111        Ok(module)
1112    }
1113
1114    fn function(
1115        &mut self,
1116        f: &ast::Function<'source>,
1117        span: Span,
1118        ctx: &mut GlobalContext<'source, '_, '_>,
1119    ) -> Result<LoweredGlobalDecl, Error<'source>> {
1120        let mut local_table = FastHashMap::default();
1121        let mut expressions = Arena::new();
1122        let mut named_expressions = FastIndexMap::default();
1123        let mut local_expression_kind_tracker = crate::proc::ExpressionKindTracker::new();
1124
1125        let arguments = f
1126            .arguments
1127            .iter()
1128            .enumerate()
1129            .map(|(i, arg)| {
1130                let ty = self.resolve_ast_type(arg.ty, ctx)?;
1131                let expr = expressions
1132                    .append(crate::Expression::FunctionArgument(i as u32), arg.name.span);
1133                local_table.insert(arg.handle, Typed::Plain(expr));
1134                named_expressions.insert(expr, (arg.name.name.to_string(), arg.name.span));
1135                local_expression_kind_tracker.insert(expr, crate::proc::ExpressionKind::Runtime);
1136
1137                Ok(crate::FunctionArgument {
1138                    name: Some(arg.name.name.to_string()),
1139                    ty,
1140                    binding: self.binding(&arg.binding, ty, ctx)?,
1141                })
1142            })
1143            .collect::<Result<Vec<_>, _>>()?;
1144
1145        let result = f
1146            .result
1147            .as_ref()
1148            .map(|res| {
1149                let ty = self.resolve_ast_type(res.ty, ctx)?;
1150                Ok(crate::FunctionResult {
1151                    ty,
1152                    binding: self.binding(&res.binding, ty, ctx)?,
1153                })
1154            })
1155            .transpose()?;
1156
1157        let mut function = crate::Function {
1158            name: Some(f.name.name.to_string()),
1159            arguments,
1160            result,
1161            local_variables: Arena::new(),
1162            expressions,
1163            named_expressions: crate::NamedExpressions::default(),
1164            body: crate::Block::default(),
1165        };
1166
1167        let mut typifier = Typifier::default();
1168        let mut stmt_ctx = StatementContext {
1169            local_table: &mut local_table,
1170            globals: ctx.globals,
1171            ast_expressions: ctx.ast_expressions,
1172            const_typifier: ctx.const_typifier,
1173            typifier: &mut typifier,
1174            function: &mut function,
1175            named_expressions: &mut named_expressions,
1176            types: ctx.types,
1177            module: ctx.module,
1178            local_expression_kind_tracker: &mut local_expression_kind_tracker,
1179            global_expression_kind_tracker: ctx.global_expression_kind_tracker,
1180        };
1181        let mut body = self.block(&f.body, false, &mut stmt_ctx)?;
1182        ensure_block_returns(&mut body);
1183
1184        function.body = body;
1185        function.named_expressions = named_expressions
1186            .into_iter()
1187            .map(|(key, (name, _))| (key, name))
1188            .collect();
1189
1190        if let Some(ref entry) = f.entry_point {
1191            let workgroup_size = if let Some(workgroup_size) = entry.workgroup_size {
1192                // TODO: replace with try_map once stabilized
1193                let mut workgroup_size_out = [1; 3];
1194                for (i, size) in workgroup_size.into_iter().enumerate() {
1195                    if let Some(size_expr) = size {
1196                        workgroup_size_out[i] = self.const_u32(size_expr, &mut ctx.as_const())?.0;
1197                    }
1198                }
1199                workgroup_size_out
1200            } else {
1201                [0; 3]
1202            };
1203
1204            ctx.module.entry_points.push(crate::EntryPoint {
1205                name: f.name.name.to_string(),
1206                stage: entry.stage,
1207                early_depth_test: entry.early_depth_test,
1208                workgroup_size,
1209                function,
1210            });
1211            Ok(LoweredGlobalDecl::EntryPoint)
1212        } else {
1213            let handle = ctx.module.functions.append(function, span);
1214            Ok(LoweredGlobalDecl::Function(handle))
1215        }
1216    }
1217
1218    fn block(
1219        &mut self,
1220        b: &ast::Block<'source>,
1221        is_inside_loop: bool,
1222        ctx: &mut StatementContext<'source, '_, '_>,
1223    ) -> Result<crate::Block, Error<'source>> {
1224        let mut block = crate::Block::default();
1225
1226        for stmt in b.stmts.iter() {
1227            self.statement(stmt, &mut block, is_inside_loop, ctx)?;
1228        }
1229
1230        Ok(block)
1231    }
1232
1233    fn statement(
1234        &mut self,
1235        stmt: &ast::Statement<'source>,
1236        block: &mut crate::Block,
1237        is_inside_loop: bool,
1238        ctx: &mut StatementContext<'source, '_, '_>,
1239    ) -> Result<(), Error<'source>> {
1240        let out = match stmt.kind {
1241            ast::StatementKind::Block(ref block) => {
1242                let block = self.block(block, is_inside_loop, ctx)?;
1243                crate::Statement::Block(block)
1244            }
1245            ast::StatementKind::LocalDecl(ref decl) => match *decl {
1246                ast::LocalDecl::Let(ref l) => {
1247                    let mut emitter = Emitter::default();
1248                    emitter.start(&ctx.function.expressions);
1249
1250                    let value =
1251                        self.expression(l.init, &mut ctx.as_expression(block, &mut emitter))?;
1252
1253                    // The WGSL spec says that any expression that refers to a
1254                    // `let`-bound variable is not a const expression. This
1255                    // affects when errors must be reported, so we can't even
1256                    // treat suitable `let` bindings as constant as an
1257                    // optimization.
1258                    ctx.local_expression_kind_tracker.force_non_const(value);
1259
1260                    let explicit_ty =
1261                        l.ty.map(|ty| self.resolve_ast_type(ty, &mut ctx.as_global()))
1262                            .transpose()?;
1263
1264                    if let Some(ty) = explicit_ty {
1265                        let mut ctx = ctx.as_expression(block, &mut emitter);
1266                        let init_ty = ctx.register_type(value)?;
1267                        if !ctx.module.types[ty]
1268                            .inner
1269                            .equivalent(&ctx.module.types[init_ty].inner, &ctx.module.types)
1270                        {
1271                            let gctx = &ctx.module.to_ctx();
1272                            return Err(Error::InitializationTypeMismatch {
1273                                name: l.name.span,
1274                                expected: ty.to_wgsl(gctx),
1275                                got: init_ty.to_wgsl(gctx),
1276                            });
1277                        }
1278                    }
1279
1280                    block.extend(emitter.finish(&ctx.function.expressions));
1281                    ctx.local_table.insert(l.handle, Typed::Plain(value));
1282                    ctx.named_expressions
1283                        .insert(value, (l.name.name.to_string(), l.name.span));
1284
1285                    return Ok(());
1286                }
1287                ast::LocalDecl::Var(ref v) => {
1288                    let explicit_ty =
1289                        v.ty.map(|ast| self.resolve_ast_type(ast, &mut ctx.as_global()))
1290                            .transpose()?;
1291
1292                    let mut emitter = Emitter::default();
1293                    emitter.start(&ctx.function.expressions);
1294                    let mut ectx = ctx.as_expression(block, &mut emitter);
1295
1296                    let ty;
1297                    let initializer;
1298                    match (v.init, explicit_ty) {
1299                        (Some(init), Some(explicit_ty)) => {
1300                            let init = self.expression_for_abstract(init, &mut ectx)?;
1301                            let ty_res = crate::proc::TypeResolution::Handle(explicit_ty);
1302                            let init = ectx
1303                                .try_automatic_conversions(init, &ty_res, v.name.span)
1304                                .map_err(|error| match error {
1305                                Error::AutoConversion {
1306                                    dest_span: _,
1307                                    dest_type,
1308                                    source_span: _,
1309                                    source_type,
1310                                } => Error::InitializationTypeMismatch {
1311                                    name: v.name.span,
1312                                    expected: dest_type,
1313                                    got: source_type,
1314                                },
1315                                other => other,
1316                            })?;
1317                            ty = explicit_ty;
1318                            initializer = Some(init);
1319                        }
1320                        (Some(init), None) => {
1321                            let concretized = self.expression(init, &mut ectx)?;
1322                            ty = ectx.register_type(concretized)?;
1323                            initializer = Some(concretized);
1324                        }
1325                        (None, Some(explicit_ty)) => {
1326                            ty = explicit_ty;
1327                            initializer = None;
1328                        }
1329                        (None, None) => return Err(Error::DeclMissingTypeAndInit(v.name.span)),
1330                    }
1331
1332                    let (const_initializer, initializer) = {
1333                        match initializer {
1334                            Some(init) => {
1335                                // It's not correct to hoist the initializer up
1336                                // to the top of the function if:
1337                                // - the initialization is inside a loop, and should
1338                                //   take place on every iteration, or
1339                                // - the initialization is not a constant
1340                                //   expression, so its value depends on the
1341                                //   state at the point of initialization.
1342                                if is_inside_loop
1343                                    || !ctx.local_expression_kind_tracker.is_const_or_override(init)
1344                                {
1345                                    (None, Some(init))
1346                                } else {
1347                                    (Some(init), None)
1348                                }
1349                            }
1350                            None => (None, None),
1351                        }
1352                    };
1353
1354                    let var = ctx.function.local_variables.append(
1355                        crate::LocalVariable {
1356                            name: Some(v.name.name.to_string()),
1357                            ty,
1358                            init: const_initializer,
1359                        },
1360                        stmt.span,
1361                    );
1362
1363                    let handle = ctx.as_expression(block, &mut emitter).interrupt_emitter(
1364                        crate::Expression::LocalVariable(var),
1365                        Span::UNDEFINED,
1366                    )?;
1367                    block.extend(emitter.finish(&ctx.function.expressions));
1368                    ctx.local_table.insert(v.handle, Typed::Reference(handle));
1369
1370                    match initializer {
1371                        Some(initializer) => crate::Statement::Store {
1372                            pointer: handle,
1373                            value: initializer,
1374                        },
1375                        None => return Ok(()),
1376                    }
1377                }
1378            },
1379            ast::StatementKind::If {
1380                condition,
1381                ref accept,
1382                ref reject,
1383            } => {
1384                let mut emitter = Emitter::default();
1385                emitter.start(&ctx.function.expressions);
1386
1387                let condition =
1388                    self.expression(condition, &mut ctx.as_expression(block, &mut emitter))?;
1389                block.extend(emitter.finish(&ctx.function.expressions));
1390
1391                let accept = self.block(accept, is_inside_loop, ctx)?;
1392                let reject = self.block(reject, is_inside_loop, ctx)?;
1393
1394                crate::Statement::If {
1395                    condition,
1396                    accept,
1397                    reject,
1398                }
1399            }
1400            ast::StatementKind::Switch {
1401                selector,
1402                ref cases,
1403            } => {
1404                let mut emitter = Emitter::default();
1405                emitter.start(&ctx.function.expressions);
1406
1407                let mut ectx = ctx.as_expression(block, &mut emitter);
1408                let selector = self.expression(selector, &mut ectx)?;
1409
1410                let uint =
1411                    resolve_inner!(ectx, selector).scalar_kind() == Some(crate::ScalarKind::Uint);
1412                block.extend(emitter.finish(&ctx.function.expressions));
1413
1414                let cases = cases
1415                    .iter()
1416                    .map(|case| {
1417                        Ok(crate::SwitchCase {
1418                            value: match case.value {
1419                                ast::SwitchValue::Expr(expr) => {
1420                                    let span = ctx.ast_expressions.get_span(expr);
1421                                    let expr =
1422                                        self.expression(expr, &mut ctx.as_global().as_const())?;
1423                                    match ctx.module.to_ctx().eval_expr_to_literal(expr) {
1424                                        Some(crate::Literal::I32(value)) if !uint => {
1425                                            crate::SwitchValue::I32(value)
1426                                        }
1427                                        Some(crate::Literal::U32(value)) if uint => {
1428                                            crate::SwitchValue::U32(value)
1429                                        }
1430                                        _ => {
1431                                            return Err(Error::InvalidSwitchValue { uint, span });
1432                                        }
1433                                    }
1434                                }
1435                                ast::SwitchValue::Default => crate::SwitchValue::Default,
1436                            },
1437                            body: self.block(&case.body, is_inside_loop, ctx)?,
1438                            fall_through: case.fall_through,
1439                        })
1440                    })
1441                    .collect::<Result<_, _>>()?;
1442
1443                crate::Statement::Switch { selector, cases }
1444            }
1445            ast::StatementKind::Loop {
1446                ref body,
1447                ref continuing,
1448                break_if,
1449            } => {
1450                let body = self.block(body, true, ctx)?;
1451                let mut continuing = self.block(continuing, true, ctx)?;
1452
1453                let mut emitter = Emitter::default();
1454                emitter.start(&ctx.function.expressions);
1455                let break_if = break_if
1456                    .map(|expr| {
1457                        self.expression(expr, &mut ctx.as_expression(&mut continuing, &mut emitter))
1458                    })
1459                    .transpose()?;
1460                continuing.extend(emitter.finish(&ctx.function.expressions));
1461
1462                crate::Statement::Loop {
1463                    body,
1464                    continuing,
1465                    break_if,
1466                }
1467            }
1468            ast::StatementKind::Break => crate::Statement::Break,
1469            ast::StatementKind::Continue => crate::Statement::Continue,
1470            ast::StatementKind::Return { value } => {
1471                let mut emitter = Emitter::default();
1472                emitter.start(&ctx.function.expressions);
1473
1474                let value = value
1475                    .map(|expr| self.expression(expr, &mut ctx.as_expression(block, &mut emitter)))
1476                    .transpose()?;
1477                block.extend(emitter.finish(&ctx.function.expressions));
1478
1479                crate::Statement::Return { value }
1480            }
1481            ast::StatementKind::Kill => crate::Statement::Kill,
1482            ast::StatementKind::Call {
1483                ref function,
1484                ref arguments,
1485            } => {
1486                let mut emitter = Emitter::default();
1487                emitter.start(&ctx.function.expressions);
1488
1489                let _ = self.call(
1490                    stmt.span,
1491                    function,
1492                    arguments,
1493                    &mut ctx.as_expression(block, &mut emitter),
1494                )?;
1495                block.extend(emitter.finish(&ctx.function.expressions));
1496                return Ok(());
1497            }
1498            ast::StatementKind::Assign {
1499                target: ast_target,
1500                op,
1501                value,
1502            } => {
1503                let mut emitter = Emitter::default();
1504                emitter.start(&ctx.function.expressions);
1505
1506                let target = self.expression_for_reference(
1507                    ast_target,
1508                    &mut ctx.as_expression(block, &mut emitter),
1509                )?;
1510                let mut value =
1511                    self.expression(value, &mut ctx.as_expression(block, &mut emitter))?;
1512
1513                let target_handle = match target {
1514                    Typed::Reference(handle) => handle,
1515                    Typed::Plain(handle) => {
1516                        let ty = ctx.invalid_assignment_type(handle);
1517                        return Err(Error::InvalidAssignment {
1518                            span: ctx.ast_expressions.get_span(ast_target),
1519                            ty,
1520                        });
1521                    }
1522                };
1523
1524                let value = match op {
1525                    Some(op) => {
1526                        let mut ctx = ctx.as_expression(block, &mut emitter);
1527                        let mut left = ctx.apply_load_rule(target)?;
1528                        ctx.binary_op_splat(op, &mut left, &mut value)?;
1529                        ctx.append_expression(
1530                            crate::Expression::Binary {
1531                                op,
1532                                left,
1533                                right: value,
1534                            },
1535                            stmt.span,
1536                        )?
1537                    }
1538                    None => value,
1539                };
1540                block.extend(emitter.finish(&ctx.function.expressions));
1541
1542                crate::Statement::Store {
1543                    pointer: target_handle,
1544                    value,
1545                }
1546            }
1547            ast::StatementKind::Increment(value) | ast::StatementKind::Decrement(value) => {
1548                let mut emitter = Emitter::default();
1549                emitter.start(&ctx.function.expressions);
1550
1551                let op = match stmt.kind {
1552                    ast::StatementKind::Increment(_) => crate::BinaryOperator::Add,
1553                    ast::StatementKind::Decrement(_) => crate::BinaryOperator::Subtract,
1554                    _ => unreachable!(),
1555                };
1556
1557                let value_span = ctx.ast_expressions.get_span(value);
1558                let target = self
1559                    .expression_for_reference(value, &mut ctx.as_expression(block, &mut emitter))?;
1560                let target_handle = match target {
1561                    Typed::Reference(handle) => handle,
1562                    Typed::Plain(_) => return Err(Error::BadIncrDecrReferenceType(value_span)),
1563                };
1564
1565                let mut ectx = ctx.as_expression(block, &mut emitter);
1566                let scalar = match *resolve_inner!(ectx, target_handle) {
1567                    crate::TypeInner::ValuePointer {
1568                        size: None, scalar, ..
1569                    } => scalar,
1570                    crate::TypeInner::Pointer { base, .. } => match ectx.module.types[base].inner {
1571                        crate::TypeInner::Scalar(scalar) => scalar,
1572                        _ => return Err(Error::BadIncrDecrReferenceType(value_span)),
1573                    },
1574                    _ => return Err(Error::BadIncrDecrReferenceType(value_span)),
1575                };
1576                let literal = match scalar.kind {
1577                    crate::ScalarKind::Sint | crate::ScalarKind::Uint => {
1578                        crate::Literal::one(scalar)
1579                            .ok_or(Error::BadIncrDecrReferenceType(value_span))?
1580                    }
1581                    _ => return Err(Error::BadIncrDecrReferenceType(value_span)),
1582                };
1583
1584                let right =
1585                    ectx.interrupt_emitter(crate::Expression::Literal(literal), Span::UNDEFINED)?;
1586                let rctx = ectx.runtime_expression_ctx(stmt.span)?;
1587                let left = rctx.function.expressions.append(
1588                    crate::Expression::Load {
1589                        pointer: target_handle,
1590                    },
1591                    value_span,
1592                );
1593                let value = rctx
1594                    .function
1595                    .expressions
1596                    .append(crate::Expression::Binary { op, left, right }, stmt.span);
1597                rctx.local_expression_kind_tracker
1598                    .insert(left, crate::proc::ExpressionKind::Runtime);
1599                rctx.local_expression_kind_tracker
1600                    .insert(value, crate::proc::ExpressionKind::Runtime);
1601
1602                block.extend(emitter.finish(&ctx.function.expressions));
1603                crate::Statement::Store {
1604                    pointer: target_handle,
1605                    value,
1606                }
1607            }
1608            ast::StatementKind::Ignore(expr) => {
1609                let mut emitter = Emitter::default();
1610                emitter.start(&ctx.function.expressions);
1611
1612                let _ = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?;
1613                block.extend(emitter.finish(&ctx.function.expressions));
1614                return Ok(());
1615            }
1616        };
1617
1618        block.push(out, stmt.span);
1619
1620        Ok(())
1621    }
1622
1623    /// Lower `expr` and apply the Load Rule if possible.
1624    ///
1625    /// For the time being, this concretizes abstract values, to support
1626    /// consumers that haven't been adapted to consume them yet. Consumers
1627    /// prepared for abstract values can call [`expression_for_abstract`].
1628    ///
1629    /// [`expression_for_abstract`]: Lowerer::expression_for_abstract
1630    fn expression(
1631        &mut self,
1632        expr: Handle<ast::Expression<'source>>,
1633        ctx: &mut ExpressionContext<'source, '_, '_>,
1634    ) -> Result<Handle<crate::Expression>, Error<'source>> {
1635        let expr = self.expression_for_abstract(expr, ctx)?;
1636        ctx.concretize(expr)
1637    }
1638
1639    fn expression_for_abstract(
1640        &mut self,
1641        expr: Handle<ast::Expression<'source>>,
1642        ctx: &mut ExpressionContext<'source, '_, '_>,
1643    ) -> Result<Handle<crate::Expression>, Error<'source>> {
1644        let expr = self.expression_for_reference(expr, ctx)?;
1645        ctx.apply_load_rule(expr)
1646    }
1647
1648    fn expression_for_reference(
1649        &mut self,
1650        expr: Handle<ast::Expression<'source>>,
1651        ctx: &mut ExpressionContext<'source, '_, '_>,
1652    ) -> Result<Typed<Handle<crate::Expression>>, Error<'source>> {
1653        let span = ctx.ast_expressions.get_span(expr);
1654        let expr = &ctx.ast_expressions[expr];
1655
1656        let expr: Typed<crate::Expression> = match *expr {
1657            ast::Expression::Literal(literal) => {
1658                let literal = match literal {
1659                    ast::Literal::Number(Number::F32(f)) => crate::Literal::F32(f),
1660                    ast::Literal::Number(Number::I32(i)) => crate::Literal::I32(i),
1661                    ast::Literal::Number(Number::U32(u)) => crate::Literal::U32(u),
1662                    ast::Literal::Number(Number::I64(i)) => crate::Literal::I64(i),
1663                    ast::Literal::Number(Number::U64(u)) => crate::Literal::U64(u),
1664                    ast::Literal::Number(Number::F64(f)) => crate::Literal::F64(f),
1665                    ast::Literal::Number(Number::AbstractInt(i)) => crate::Literal::AbstractInt(i),
1666                    ast::Literal::Number(Number::AbstractFloat(f)) => {
1667                        crate::Literal::AbstractFloat(f)
1668                    }
1669                    ast::Literal::Bool(b) => crate::Literal::Bool(b),
1670                };
1671                let handle = ctx.interrupt_emitter(crate::Expression::Literal(literal), span)?;
1672                return Ok(Typed::Plain(handle));
1673            }
1674            ast::Expression::Ident(ast::IdentExpr::Local(local)) => {
1675                let rctx = ctx.runtime_expression_ctx(span)?;
1676                return Ok(rctx.local_table[&local]);
1677            }
1678            ast::Expression::Ident(ast::IdentExpr::Unresolved(name)) => {
1679                let global = ctx
1680                    .globals
1681                    .get(name)
1682                    .ok_or(Error::UnknownIdent(span, name))?;
1683                let expr = match *global {
1684                    LoweredGlobalDecl::Var(handle) => {
1685                        let expr = crate::Expression::GlobalVariable(handle);
1686                        match ctx.module.global_variables[handle].space {
1687                            crate::AddressSpace::Handle => Typed::Plain(expr),
1688                            _ => Typed::Reference(expr),
1689                        }
1690                    }
1691                    LoweredGlobalDecl::Const(handle) => {
1692                        Typed::Plain(crate::Expression::Constant(handle))
1693                    }
1694                    LoweredGlobalDecl::Override(handle) => {
1695                        Typed::Plain(crate::Expression::Override(handle))
1696                    }
1697                    LoweredGlobalDecl::Function(_)
1698                    | LoweredGlobalDecl::Type(_)
1699                    | LoweredGlobalDecl::EntryPoint => {
1700                        return Err(Error::Unexpected(span, ExpectedToken::Variable));
1701                    }
1702                };
1703
1704                return expr.try_map(|handle| ctx.interrupt_emitter(handle, span));
1705            }
1706            ast::Expression::Construct {
1707                ref ty,
1708                ty_span,
1709                ref components,
1710            } => {
1711                let handle = self.construct(span, ty, ty_span, components, ctx)?;
1712                return Ok(Typed::Plain(handle));
1713            }
1714            ast::Expression::Unary { op, expr } => {
1715                let expr = self.expression_for_abstract(expr, ctx)?;
1716                Typed::Plain(crate::Expression::Unary { op, expr })
1717            }
1718            ast::Expression::AddrOf(expr) => {
1719                // The `&` operator simply converts a reference to a pointer. And since a
1720                // reference is required, the Load Rule is not applied.
1721                match self.expression_for_reference(expr, ctx)? {
1722                    Typed::Reference(handle) => {
1723                        // No code is generated. We just declare the reference a pointer now.
1724                        return Ok(Typed::Plain(handle));
1725                    }
1726                    Typed::Plain(_) => {
1727                        return Err(Error::NotReference("the operand of the `&` operator", span));
1728                    }
1729                }
1730            }
1731            ast::Expression::Deref(expr) => {
1732                // The pointer we dereference must be loaded.
1733                let pointer = self.expression(expr, ctx)?;
1734
1735                if resolve_inner!(ctx, pointer).pointer_space().is_none() {
1736                    return Err(Error::NotPointer(span));
1737                }
1738
1739                // No code is generated. We just declare the pointer a reference now.
1740                return Ok(Typed::Reference(pointer));
1741            }
1742            ast::Expression::Binary { op, left, right } => {
1743                self.binary(op, left, right, span, ctx)?
1744            }
1745            ast::Expression::Call {
1746                ref function,
1747                ref arguments,
1748            } => {
1749                let handle = self
1750                    .call(span, function, arguments, ctx)?
1751                    .ok_or(Error::FunctionReturnsVoid(function.span))?;
1752                return Ok(Typed::Plain(handle));
1753            }
1754            ast::Expression::Index { base, index } => {
1755                let lowered_base = self.expression_for_reference(base, ctx)?;
1756                let index = self.expression(index, ctx)?;
1757
1758                if let Typed::Plain(handle) = lowered_base {
1759                    if resolve_inner!(ctx, handle).pointer_space().is_some() {
1760                        return Err(Error::Pointer(
1761                            "the value indexed by a `[]` subscripting expression",
1762                            ctx.ast_expressions.get_span(base),
1763                        ));
1764                    }
1765                }
1766
1767                lowered_base.map(|base| match ctx.const_access(index) {
1768                    Some(index) => crate::Expression::AccessIndex { base, index },
1769                    None => crate::Expression::Access { base, index },
1770                })
1771            }
1772            ast::Expression::Member { base, ref field } => {
1773                let lowered_base = self.expression_for_reference(base, ctx)?;
1774
1775                let temp_inner;
1776                let composite_type: &crate::TypeInner = match lowered_base {
1777                    Typed::Reference(handle) => {
1778                        let inner = resolve_inner!(ctx, handle);
1779                        match *inner {
1780                            crate::TypeInner::Pointer { base, .. } => &ctx.module.types[base].inner,
1781                            crate::TypeInner::ValuePointer {
1782                                size: None, scalar, ..
1783                            } => {
1784                                temp_inner = crate::TypeInner::Scalar(scalar);
1785                                &temp_inner
1786                            }
1787                            crate::TypeInner::ValuePointer {
1788                                size: Some(size),
1789                                scalar,
1790                                ..
1791                            } => {
1792                                temp_inner = crate::TypeInner::Vector { size, scalar };
1793                                &temp_inner
1794                            }
1795                            _ => unreachable!(
1796                                "In Typed::Reference(handle), handle must be a Naga pointer"
1797                            ),
1798                        }
1799                    }
1800
1801                    Typed::Plain(handle) => {
1802                        let inner = resolve_inner!(ctx, handle);
1803                        if let crate::TypeInner::Pointer { .. }
1804                        | crate::TypeInner::ValuePointer { .. } = *inner
1805                        {
1806                            return Err(Error::Pointer(
1807                                "the value accessed by a `.member` expression",
1808                                ctx.ast_expressions.get_span(base),
1809                            ));
1810                        }
1811                        inner
1812                    }
1813                };
1814
1815                let access = match *composite_type {
1816                    crate::TypeInner::Struct { ref members, .. } => {
1817                        let index = members
1818                            .iter()
1819                            .position(|m| m.name.as_deref() == Some(field.name))
1820                            .ok_or(Error::BadAccessor(field.span))?
1821                            as u32;
1822
1823                        lowered_base.map(|base| crate::Expression::AccessIndex { base, index })
1824                    }
1825                    crate::TypeInner::Vector { .. } | crate::TypeInner::Matrix { .. } => {
1826                        match Components::new(field.name, field.span)? {
1827                            Components::Swizzle { size, pattern } => {
1828                                // Swizzles aren't allowed on matrices, but
1829                                // validation will catch that.
1830                                Typed::Plain(crate::Expression::Swizzle {
1831                                    size,
1832                                    vector: ctx.apply_load_rule(lowered_base)?,
1833                                    pattern,
1834                                })
1835                            }
1836                            Components::Single(index) => lowered_base
1837                                .map(|base| crate::Expression::AccessIndex { base, index }),
1838                        }
1839                    }
1840                    _ => return Err(Error::BadAccessor(field.span)),
1841                };
1842
1843                access
1844            }
1845            ast::Expression::Bitcast { expr, to, ty_span } => {
1846                let expr = self.expression(expr, ctx)?;
1847                let to_resolved = self.resolve_ast_type(to, &mut ctx.as_global())?;
1848
1849                let element_scalar = match ctx.module.types[to_resolved].inner {
1850                    crate::TypeInner::Scalar(scalar) => scalar,
1851                    crate::TypeInner::Vector { scalar, .. } => scalar,
1852                    _ => {
1853                        let ty = resolve!(ctx, expr);
1854                        let gctx = &ctx.module.to_ctx();
1855                        return Err(Error::BadTypeCast {
1856                            from_type: ty.to_wgsl(gctx),
1857                            span: ty_span,
1858                            to_type: to_resolved.to_wgsl(gctx),
1859                        });
1860                    }
1861                };
1862
1863                Typed::Plain(crate::Expression::As {
1864                    expr,
1865                    kind: element_scalar.kind,
1866                    convert: None,
1867                })
1868            }
1869        };
1870
1871        expr.try_map(|handle| ctx.append_expression(handle, span))
1872    }
1873
1874    fn binary(
1875        &mut self,
1876        op: crate::BinaryOperator,
1877        left: Handle<ast::Expression<'source>>,
1878        right: Handle<ast::Expression<'source>>,
1879        span: Span,
1880        ctx: &mut ExpressionContext<'source, '_, '_>,
1881    ) -> Result<Typed<crate::Expression>, Error<'source>> {
1882        // Load both operands.
1883        let mut left = self.expression_for_abstract(left, ctx)?;
1884        let mut right = self.expression_for_abstract(right, ctx)?;
1885
1886        // Convert `scalar op vector` to `vector op vector` by introducing
1887        // `Splat` expressions.
1888        ctx.binary_op_splat(op, &mut left, &mut right)?;
1889
1890        // Apply automatic conversions.
1891        match op {
1892            // Shift operators require the right operand to be `u32` or
1893            // `vecN<u32>`. We can let the validator sort out vector length
1894            // issues, but the right operand must be, or convert to, a u32 leaf
1895            // scalar.
1896            crate::BinaryOperator::ShiftLeft | crate::BinaryOperator::ShiftRight => {
1897                right =
1898                    ctx.try_automatic_conversion_for_leaf_scalar(right, crate::Scalar::U32, span)?;
1899            }
1900
1901            // All other operators follow the same pattern: reconcile the
1902            // scalar leaf types. If there's no reconciliation possible,
1903            // leave the expressions as they are: validation will report the
1904            // problem.
1905            _ => {
1906                ctx.grow_types(left)?;
1907                ctx.grow_types(right)?;
1908                if let Ok(consensus_scalar) =
1909                    ctx.automatic_conversion_consensus([left, right].iter())
1910                {
1911                    ctx.convert_to_leaf_scalar(&mut left, consensus_scalar)?;
1912                    ctx.convert_to_leaf_scalar(&mut right, consensus_scalar)?;
1913                }
1914            }
1915        }
1916
1917        Ok(Typed::Plain(crate::Expression::Binary { op, left, right }))
1918    }
1919
1920    /// Generate Naga IR for call expressions and statements, and type
1921    /// constructor expressions.
1922    ///
1923    /// The "function" being called is simply an `Ident` that we know refers to
1924    /// some module-scope definition.
1925    ///
1926    /// - If it is the name of a type, then the expression is a type constructor
1927    ///   expression: either constructing a value from components, a conversion
1928    ///   expression, or a zero value expression.
1929    ///
1930    /// - If it is the name of a function, then we're generating a [`Call`]
1931    ///   statement. We may be in the midst of generating code for an
1932    ///   expression, in which case we must generate an `Emit` statement to
1933    ///   force evaluation of the IR expressions we've generated so far, add the
1934    ///   `Call` statement to the current block, and then resume generating
1935    ///   expressions.
1936    ///
1937    /// [`Call`]: crate::Statement::Call
1938    fn call(
1939        &mut self,
1940        span: Span,
1941        function: &ast::Ident<'source>,
1942        arguments: &[Handle<ast::Expression<'source>>],
1943        ctx: &mut ExpressionContext<'source, '_, '_>,
1944    ) -> Result<Option<Handle<crate::Expression>>, Error<'source>> {
1945        match ctx.globals.get(function.name) {
1946            Some(&LoweredGlobalDecl::Type(ty)) => {
1947                let handle = self.construct(
1948                    span,
1949                    &ast::ConstructorType::Type(ty),
1950                    function.span,
1951                    arguments,
1952                    ctx,
1953                )?;
1954                Ok(Some(handle))
1955            }
1956            Some(
1957                &LoweredGlobalDecl::Const(_)
1958                | &LoweredGlobalDecl::Override(_)
1959                | &LoweredGlobalDecl::Var(_),
1960            ) => Err(Error::Unexpected(function.span, ExpectedToken::Function)),
1961            Some(&LoweredGlobalDecl::EntryPoint) => Err(Error::CalledEntryPoint(function.span)),
1962            Some(&LoweredGlobalDecl::Function(function)) => {
1963                let arguments = arguments
1964                    .iter()
1965                    .map(|&arg| self.expression(arg, ctx))
1966                    .collect::<Result<Vec<_>, _>>()?;
1967
1968                let has_result = ctx.module.functions[function].result.is_some();
1969                let rctx = ctx.runtime_expression_ctx(span)?;
1970                // we need to always do this before a fn call since all arguments need to be emitted before the fn call
1971                rctx.block
1972                    .extend(rctx.emitter.finish(&rctx.function.expressions));
1973                let result = has_result.then(|| {
1974                    let result = rctx
1975                        .function
1976                        .expressions
1977                        .append(crate::Expression::CallResult(function), span);
1978                    rctx.local_expression_kind_tracker
1979                        .insert(result, crate::proc::ExpressionKind::Runtime);
1980                    result
1981                });
1982                rctx.emitter.start(&rctx.function.expressions);
1983                rctx.block.push(
1984                    crate::Statement::Call {
1985                        function,
1986                        arguments,
1987                        result,
1988                    },
1989                    span,
1990                );
1991
1992                Ok(result)
1993            }
1994            None => {
1995                let span = function.span;
1996                let expr = if let Some(fun) = conv::map_relational_fun(function.name) {
1997                    let mut args = ctx.prepare_args(arguments, 1, span);
1998                    let argument = self.expression(args.next()?, ctx)?;
1999                    args.finish()?;
2000
2001                    // Check for no-op all(bool) and any(bool):
2002                    let argument_unmodified = matches!(
2003                        fun,
2004                        crate::RelationalFunction::All | crate::RelationalFunction::Any
2005                    ) && {
2006                        matches!(
2007                            resolve_inner!(ctx, argument),
2008                            &crate::TypeInner::Scalar(crate::Scalar {
2009                                kind: crate::ScalarKind::Bool,
2010                                ..
2011                            })
2012                        )
2013                    };
2014
2015                    if argument_unmodified {
2016                        return Ok(Some(argument));
2017                    } else {
2018                        crate::Expression::Relational { fun, argument }
2019                    }
2020                } else if let Some((axis, ctrl)) = conv::map_derivative(function.name) {
2021                    let mut args = ctx.prepare_args(arguments, 1, span);
2022                    let expr = self.expression(args.next()?, ctx)?;
2023                    args.finish()?;
2024
2025                    crate::Expression::Derivative { axis, ctrl, expr }
2026                } else if let Some(fun) = conv::map_standard_fun(function.name) {
2027                    let expected = fun.argument_count() as _;
2028                    let mut args = ctx.prepare_args(arguments, expected, span);
2029
2030                    let arg = self.expression(args.next()?, ctx)?;
2031                    let arg1 = args
2032                        .next()
2033                        .map(|x| self.expression(x, ctx))
2034                        .ok()
2035                        .transpose()?;
2036                    let arg2 = args
2037                        .next()
2038                        .map(|x| self.expression(x, ctx))
2039                        .ok()
2040                        .transpose()?;
2041                    let arg3 = args
2042                        .next()
2043                        .map(|x| self.expression(x, ctx))
2044                        .ok()
2045                        .transpose()?;
2046
2047                    args.finish()?;
2048
2049                    if fun == crate::MathFunction::Modf || fun == crate::MathFunction::Frexp {
2050                        if let Some((size, width)) = match *resolve_inner!(ctx, arg) {
2051                            crate::TypeInner::Scalar(crate::Scalar { width, .. }) => {
2052                                Some((None, width))
2053                            }
2054                            crate::TypeInner::Vector {
2055                                size,
2056                                scalar: crate::Scalar { width, .. },
2057                                ..
2058                            } => Some((Some(size), width)),
2059                            _ => None,
2060                        } {
2061                            ctx.module.generate_predeclared_type(
2062                                if fun == crate::MathFunction::Modf {
2063                                    crate::PredeclaredType::ModfResult { size, width }
2064                                } else {
2065                                    crate::PredeclaredType::FrexpResult { size, width }
2066                                },
2067                            );
2068                        }
2069                    }
2070
2071                    crate::Expression::Math {
2072                        fun,
2073                        arg,
2074                        arg1,
2075                        arg2,
2076                        arg3,
2077                    }
2078                } else if let Some(fun) = Texture::map(function.name) {
2079                    self.texture_sample_helper(fun, arguments, span, ctx)?
2080                } else if let Some((op, cop)) = conv::map_subgroup_operation(function.name) {
2081                    return Ok(Some(
2082                        self.subgroup_operation_helper(span, op, cop, arguments, ctx)?,
2083                    ));
2084                } else if let Some(mode) = SubgroupGather::map(function.name) {
2085                    return Ok(Some(
2086                        self.subgroup_gather_helper(span, mode, arguments, ctx)?,
2087                    ));
2088                } else if let Some(fun) = crate::AtomicFunction::map(function.name) {
2089                    return Ok(Some(self.atomic_helper(span, fun, arguments, ctx)?));
2090                } else {
2091                    match function.name {
2092                        "select" => {
2093                            let mut args = ctx.prepare_args(arguments, 3, span);
2094
2095                            let reject = self.expression(args.next()?, ctx)?;
2096                            let accept = self.expression(args.next()?, ctx)?;
2097                            let condition = self.expression(args.next()?, ctx)?;
2098
2099                            args.finish()?;
2100
2101                            crate::Expression::Select {
2102                                reject,
2103                                accept,
2104                                condition,
2105                            }
2106                        }
2107                        "arrayLength" => {
2108                            let mut args = ctx.prepare_args(arguments, 1, span);
2109                            let expr = self.expression(args.next()?, ctx)?;
2110                            args.finish()?;
2111
2112                            crate::Expression::ArrayLength(expr)
2113                        }
2114                        "atomicLoad" => {
2115                            let mut args = ctx.prepare_args(arguments, 1, span);
2116                            let pointer = self.atomic_pointer(args.next()?, ctx)?;
2117                            args.finish()?;
2118
2119                            crate::Expression::Load { pointer }
2120                        }
2121                        "atomicStore" => {
2122                            let mut args = ctx.prepare_args(arguments, 2, span);
2123                            let pointer = self.atomic_pointer(args.next()?, ctx)?;
2124                            let value = self.expression(args.next()?, ctx)?;
2125                            args.finish()?;
2126
2127                            let rctx = ctx.runtime_expression_ctx(span)?;
2128                            rctx.block
2129                                .extend(rctx.emitter.finish(&rctx.function.expressions));
2130                            rctx.emitter.start(&rctx.function.expressions);
2131                            rctx.block
2132                                .push(crate::Statement::Store { pointer, value }, span);
2133                            return Ok(None);
2134                        }
2135                        "atomicCompareExchangeWeak" => {
2136                            let mut args = ctx.prepare_args(arguments, 3, span);
2137
2138                            let pointer = self.atomic_pointer(args.next()?, ctx)?;
2139
2140                            let compare = self.expression(args.next()?, ctx)?;
2141
2142                            let value = args.next()?;
2143                            let value_span = ctx.ast_expressions.get_span(value);
2144                            let value = self.expression(value, ctx)?;
2145
2146                            args.finish()?;
2147
2148                            let expression = match *resolve_inner!(ctx, value) {
2149                                crate::TypeInner::Scalar(scalar) => {
2150                                    crate::Expression::AtomicResult {
2151                                        ty: ctx.module.generate_predeclared_type(
2152                                            crate::PredeclaredType::AtomicCompareExchangeWeakResult(
2153                                                scalar,
2154                                            ),
2155                                        ),
2156                                        comparison: true,
2157                                    }
2158                                }
2159                                _ => return Err(Error::InvalidAtomicOperandType(value_span)),
2160                            };
2161
2162                            let result = ctx.interrupt_emitter(expression, span)?;
2163                            let rctx = ctx.runtime_expression_ctx(span)?;
2164                            rctx.block.push(
2165                                crate::Statement::Atomic {
2166                                    pointer,
2167                                    fun: crate::AtomicFunction::Exchange {
2168                                        compare: Some(compare),
2169                                    },
2170                                    value,
2171                                    result,
2172                                },
2173                                span,
2174                            );
2175                            return Ok(Some(result));
2176                        }
2177                        "storageBarrier" => {
2178                            ctx.prepare_args(arguments, 0, span).finish()?;
2179
2180                            let rctx = ctx.runtime_expression_ctx(span)?;
2181                            rctx.block
2182                                .push(crate::Statement::Barrier(crate::Barrier::STORAGE), span);
2183                            return Ok(None);
2184                        }
2185                        "workgroupBarrier" => {
2186                            ctx.prepare_args(arguments, 0, span).finish()?;
2187
2188                            let rctx = ctx.runtime_expression_ctx(span)?;
2189                            rctx.block
2190                                .push(crate::Statement::Barrier(crate::Barrier::WORK_GROUP), span);
2191                            return Ok(None);
2192                        }
2193                        "subgroupBarrier" => {
2194                            ctx.prepare_args(arguments, 0, span).finish()?;
2195
2196                            let rctx = ctx.runtime_expression_ctx(span)?;
2197                            rctx.block
2198                                .push(crate::Statement::Barrier(crate::Barrier::SUB_GROUP), span);
2199                            return Ok(None);
2200                        }
2201                        "workgroupUniformLoad" => {
2202                            let mut args = ctx.prepare_args(arguments, 1, span);
2203                            let expr = args.next()?;
2204                            args.finish()?;
2205
2206                            let pointer = self.expression(expr, ctx)?;
2207                            let result_ty = match *resolve_inner!(ctx, pointer) {
2208                                crate::TypeInner::Pointer {
2209                                    base,
2210                                    space: crate::AddressSpace::WorkGroup,
2211                                } => base,
2212                                ref other => {
2213                                    log::error!("Type {other:?} passed to workgroupUniformLoad");
2214                                    let span = ctx.ast_expressions.get_span(expr);
2215                                    return Err(Error::InvalidWorkGroupUniformLoad(span));
2216                                }
2217                            };
2218                            let result = ctx.interrupt_emitter(
2219                                crate::Expression::WorkGroupUniformLoadResult { ty: result_ty },
2220                                span,
2221                            )?;
2222                            let rctx = ctx.runtime_expression_ctx(span)?;
2223                            rctx.block.push(
2224                                crate::Statement::WorkGroupUniformLoad { pointer, result },
2225                                span,
2226                            );
2227
2228                            return Ok(Some(result));
2229                        }
2230                        "textureStore" => {
2231                            let mut args = ctx.prepare_args(arguments, 3, span);
2232
2233                            let image = args.next()?;
2234                            let image_span = ctx.ast_expressions.get_span(image);
2235                            let image = self.expression(image, ctx)?;
2236
2237                            let coordinate = self.expression(args.next()?, ctx)?;
2238
2239                            let (_, arrayed) = ctx.image_data(image, image_span)?;
2240                            let array_index = arrayed
2241                                .then(|| {
2242                                    args.min_args += 1;
2243                                    self.expression(args.next()?, ctx)
2244                                })
2245                                .transpose()?;
2246
2247                            let value = self.expression(args.next()?, ctx)?;
2248
2249                            args.finish()?;
2250
2251                            let rctx = ctx.runtime_expression_ctx(span)?;
2252                            rctx.block
2253                                .extend(rctx.emitter.finish(&rctx.function.expressions));
2254                            rctx.emitter.start(&rctx.function.expressions);
2255                            let stmt = crate::Statement::ImageStore {
2256                                image,
2257                                coordinate,
2258                                array_index,
2259                                value,
2260                            };
2261                            rctx.block.push(stmt, span);
2262                            return Ok(None);
2263                        }
2264                        "textureLoad" => {
2265                            let mut args = ctx.prepare_args(arguments, 2, span);
2266
2267                            let image = args.next()?;
2268                            let image_span = ctx.ast_expressions.get_span(image);
2269                            let image = self.expression(image, ctx)?;
2270
2271                            let coordinate = self.expression(args.next()?, ctx)?;
2272
2273                            let (class, arrayed) = ctx.image_data(image, image_span)?;
2274                            let array_index = arrayed
2275                                .then(|| {
2276                                    args.min_args += 1;
2277                                    self.expression(args.next()?, ctx)
2278                                })
2279                                .transpose()?;
2280
2281                            let level = class
2282                                .is_mipmapped()
2283                                .then(|| {
2284                                    args.min_args += 1;
2285                                    self.expression(args.next()?, ctx)
2286                                })
2287                                .transpose()?;
2288
2289                            let sample = class
2290                                .is_multisampled()
2291                                .then(|| self.expression(args.next()?, ctx))
2292                                .transpose()?;
2293
2294                            args.finish()?;
2295
2296                            crate::Expression::ImageLoad {
2297                                image,
2298                                coordinate,
2299                                array_index,
2300                                level,
2301                                sample,
2302                            }
2303                        }
2304                        "textureDimensions" => {
2305                            let mut args = ctx.prepare_args(arguments, 1, span);
2306                            let image = self.expression(args.next()?, ctx)?;
2307                            let level = args
2308                                .next()
2309                                .map(|arg| self.expression(arg, ctx))
2310                                .ok()
2311                                .transpose()?;
2312                            args.finish()?;
2313
2314                            crate::Expression::ImageQuery {
2315                                image,
2316                                query: crate::ImageQuery::Size { level },
2317                            }
2318                        }
2319                        "textureNumLevels" => {
2320                            let mut args = ctx.prepare_args(arguments, 1, span);
2321                            let image = self.expression(args.next()?, ctx)?;
2322                            args.finish()?;
2323
2324                            crate::Expression::ImageQuery {
2325                                image,
2326                                query: crate::ImageQuery::NumLevels,
2327                            }
2328                        }
2329                        "textureNumLayers" => {
2330                            let mut args = ctx.prepare_args(arguments, 1, span);
2331                            let image = self.expression(args.next()?, ctx)?;
2332                            args.finish()?;
2333
2334                            crate::Expression::ImageQuery {
2335                                image,
2336                                query: crate::ImageQuery::NumLayers,
2337                            }
2338                        }
2339                        "textureNumSamples" => {
2340                            let mut args = ctx.prepare_args(arguments, 1, span);
2341                            let image = self.expression(args.next()?, ctx)?;
2342                            args.finish()?;
2343
2344                            crate::Expression::ImageQuery {
2345                                image,
2346                                query: crate::ImageQuery::NumSamples,
2347                            }
2348                        }
2349                        "rayQueryInitialize" => {
2350                            let mut args = ctx.prepare_args(arguments, 3, span);
2351                            let query = self.ray_query_pointer(args.next()?, ctx)?;
2352                            let acceleration_structure = self.expression(args.next()?, ctx)?;
2353                            let descriptor = self.expression(args.next()?, ctx)?;
2354                            args.finish()?;
2355
2356                            let _ = ctx.module.generate_ray_desc_type();
2357                            let fun = crate::RayQueryFunction::Initialize {
2358                                acceleration_structure,
2359                                descriptor,
2360                            };
2361
2362                            let rctx = ctx.runtime_expression_ctx(span)?;
2363                            rctx.block
2364                                .extend(rctx.emitter.finish(&rctx.function.expressions));
2365                            rctx.emitter.start(&rctx.function.expressions);
2366                            rctx.block
2367                                .push(crate::Statement::RayQuery { query, fun }, span);
2368                            return Ok(None);
2369                        }
2370                        "rayQueryProceed" => {
2371                            let mut args = ctx.prepare_args(arguments, 1, span);
2372                            let query = self.ray_query_pointer(args.next()?, ctx)?;
2373                            args.finish()?;
2374
2375                            let result = ctx.interrupt_emitter(
2376                                crate::Expression::RayQueryProceedResult,
2377                                span,
2378                            )?;
2379                            let fun = crate::RayQueryFunction::Proceed { result };
2380                            let rctx = ctx.runtime_expression_ctx(span)?;
2381                            rctx.block
2382                                .push(crate::Statement::RayQuery { query, fun }, span);
2383                            return Ok(Some(result));
2384                        }
2385                        "rayQueryGetCommittedIntersection" => {
2386                            let mut args = ctx.prepare_args(arguments, 1, span);
2387                            let query = self.ray_query_pointer(args.next()?, ctx)?;
2388                            args.finish()?;
2389
2390                            let _ = ctx.module.generate_ray_intersection_type();
2391
2392                            crate::Expression::RayQueryGetIntersection {
2393                                query,
2394                                committed: true,
2395                            }
2396                        }
2397                        "RayDesc" => {
2398                            let ty = ctx.module.generate_ray_desc_type();
2399                            let handle = self.construct(
2400                                span,
2401                                &ast::ConstructorType::Type(ty),
2402                                function.span,
2403                                arguments,
2404                                ctx,
2405                            )?;
2406                            return Ok(Some(handle));
2407                        }
2408                        "subgroupBallot" => {
2409                            let mut args = ctx.prepare_args(arguments, 0, span);
2410                            let predicate = if arguments.len() == 1 {
2411                                Some(self.expression(args.next()?, ctx)?)
2412                            } else {
2413                                None
2414                            };
2415                            args.finish()?;
2416
2417                            let result = ctx
2418                                .interrupt_emitter(crate::Expression::SubgroupBallotResult, span)?;
2419                            let rctx = ctx.runtime_expression_ctx(span)?;
2420                            rctx.block
2421                                .push(crate::Statement::SubgroupBallot { result, predicate }, span);
2422                            return Ok(Some(result));
2423                        }
2424                        _ => return Err(Error::UnknownIdent(function.span, function.name)),
2425                    }
2426                };
2427
2428                let expr = ctx.append_expression(expr, span)?;
2429                Ok(Some(expr))
2430            }
2431        }
2432    }
2433
2434    fn atomic_pointer(
2435        &mut self,
2436        expr: Handle<ast::Expression<'source>>,
2437        ctx: &mut ExpressionContext<'source, '_, '_>,
2438    ) -> Result<Handle<crate::Expression>, Error<'source>> {
2439        let span = ctx.ast_expressions.get_span(expr);
2440        let pointer = self.expression(expr, ctx)?;
2441
2442        match *resolve_inner!(ctx, pointer) {
2443            crate::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
2444                crate::TypeInner::Atomic { .. } => Ok(pointer),
2445                ref other => {
2446                    log::error!("Pointer type to {:?} passed to atomic op", other);
2447                    Err(Error::InvalidAtomicPointer(span))
2448                }
2449            },
2450            ref other => {
2451                log::error!("Type {:?} passed to atomic op", other);
2452                Err(Error::InvalidAtomicPointer(span))
2453            }
2454        }
2455    }
2456
2457    fn atomic_helper(
2458        &mut self,
2459        span: Span,
2460        fun: crate::AtomicFunction,
2461        args: &[Handle<ast::Expression<'source>>],
2462        ctx: &mut ExpressionContext<'source, '_, '_>,
2463    ) -> Result<Handle<crate::Expression>, Error<'source>> {
2464        let mut args = ctx.prepare_args(args, 2, span);
2465
2466        let pointer = self.atomic_pointer(args.next()?, ctx)?;
2467
2468        let value = args.next()?;
2469        let value = self.expression(value, ctx)?;
2470        let ty = ctx.register_type(value)?;
2471
2472        args.finish()?;
2473
2474        let result = ctx.interrupt_emitter(
2475            crate::Expression::AtomicResult {
2476                ty,
2477                comparison: false,
2478            },
2479            span,
2480        )?;
2481        let rctx = ctx.runtime_expression_ctx(span)?;
2482        rctx.block.push(
2483            crate::Statement::Atomic {
2484                pointer,
2485                fun,
2486                value,
2487                result,
2488            },
2489            span,
2490        );
2491        Ok(result)
2492    }
2493
2494    fn texture_sample_helper(
2495        &mut self,
2496        fun: Texture,
2497        args: &[Handle<ast::Expression<'source>>],
2498        span: Span,
2499        ctx: &mut ExpressionContext<'source, '_, '_>,
2500    ) -> Result<crate::Expression, Error<'source>> {
2501        let mut args = ctx.prepare_args(args, fun.min_argument_count(), span);
2502
2503        fn get_image_and_span<'source>(
2504            lowerer: &mut Lowerer<'source, '_>,
2505            args: &mut ArgumentContext<'_, 'source>,
2506            ctx: &mut ExpressionContext<'source, '_, '_>,
2507        ) -> Result<(Handle<crate::Expression>, Span), Error<'source>> {
2508            let image = args.next()?;
2509            let image_span = ctx.ast_expressions.get_span(image);
2510            let image = lowerer.expression(image, ctx)?;
2511            Ok((image, image_span))
2512        }
2513
2514        let (image, image_span, gather) = match fun {
2515            Texture::Gather => {
2516                let image_or_component = args.next()?;
2517                let image_or_component_span = ctx.ast_expressions.get_span(image_or_component);
2518                // Gathers from depth textures don't take an initial `component` argument.
2519                let lowered_image_or_component = self.expression(image_or_component, ctx)?;
2520
2521                match *resolve_inner!(ctx, lowered_image_or_component) {
2522                    crate::TypeInner::Image {
2523                        class: crate::ImageClass::Depth { .. },
2524                        ..
2525                    } => (
2526                        lowered_image_or_component,
2527                        image_or_component_span,
2528                        Some(crate::SwizzleComponent::X),
2529                    ),
2530                    _ => {
2531                        let (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
2532                        (
2533                            image,
2534                            image_span,
2535                            Some(ctx.gather_component(
2536                                lowered_image_or_component,
2537                                image_or_component_span,
2538                                span,
2539                            )?),
2540                        )
2541                    }
2542                }
2543            }
2544            Texture::GatherCompare => {
2545                let (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
2546                (image, image_span, Some(crate::SwizzleComponent::X))
2547            }
2548
2549            _ => {
2550                let (image, image_span) = get_image_and_span(self, &mut args, ctx)?;
2551                (image, image_span, None)
2552            }
2553        };
2554
2555        let sampler = self.expression(args.next()?, ctx)?;
2556
2557        let coordinate = self.expression(args.next()?, ctx)?;
2558
2559        let (_, arrayed) = ctx.image_data(image, image_span)?;
2560        let array_index = arrayed
2561            .then(|| self.expression(args.next()?, ctx))
2562            .transpose()?;
2563
2564        let (level, depth_ref) = match fun {
2565            Texture::Gather => (crate::SampleLevel::Zero, None),
2566            Texture::GatherCompare => {
2567                let reference = self.expression(args.next()?, ctx)?;
2568                (crate::SampleLevel::Zero, Some(reference))
2569            }
2570
2571            Texture::Sample => (crate::SampleLevel::Auto, None),
2572            Texture::SampleBias => {
2573                let bias = self.expression(args.next()?, ctx)?;
2574                (crate::SampleLevel::Bias(bias), None)
2575            }
2576            Texture::SampleCompare => {
2577                let reference = self.expression(args.next()?, ctx)?;
2578                (crate::SampleLevel::Auto, Some(reference))
2579            }
2580            Texture::SampleCompareLevel => {
2581                let reference = self.expression(args.next()?, ctx)?;
2582                (crate::SampleLevel::Zero, Some(reference))
2583            }
2584            Texture::SampleGrad => {
2585                let x = self.expression(args.next()?, ctx)?;
2586                let y = self.expression(args.next()?, ctx)?;
2587                (crate::SampleLevel::Gradient { x, y }, None)
2588            }
2589            Texture::SampleLevel => {
2590                let level = self.expression(args.next()?, ctx)?;
2591                (crate::SampleLevel::Exact(level), None)
2592            }
2593        };
2594
2595        let offset = args
2596            .next()
2597            .map(|arg| self.expression(arg, &mut ctx.as_const()))
2598            .ok()
2599            .transpose()?;
2600
2601        args.finish()?;
2602
2603        Ok(crate::Expression::ImageSample {
2604            image,
2605            sampler,
2606            gather,
2607            coordinate,
2608            array_index,
2609            offset,
2610            level,
2611            depth_ref,
2612        })
2613    }
2614
2615    fn subgroup_operation_helper(
2616        &mut self,
2617        span: Span,
2618        op: crate::SubgroupOperation,
2619        collective_op: crate::CollectiveOperation,
2620        arguments: &[Handle<ast::Expression<'source>>],
2621        ctx: &mut ExpressionContext<'source, '_, '_>,
2622    ) -> Result<Handle<crate::Expression>, Error<'source>> {
2623        let mut args = ctx.prepare_args(arguments, 1, span);
2624
2625        let argument = self.expression(args.next()?, ctx)?;
2626        args.finish()?;
2627
2628        let ty = ctx.register_type(argument)?;
2629
2630        let result =
2631            ctx.interrupt_emitter(crate::Expression::SubgroupOperationResult { ty }, span)?;
2632        let rctx = ctx.runtime_expression_ctx(span)?;
2633        rctx.block.push(
2634            crate::Statement::SubgroupCollectiveOperation {
2635                op,
2636                collective_op,
2637                argument,
2638                result,
2639            },
2640            span,
2641        );
2642        Ok(result)
2643    }
2644
2645    fn subgroup_gather_helper(
2646        &mut self,
2647        span: Span,
2648        mode: SubgroupGather,
2649        arguments: &[Handle<ast::Expression<'source>>],
2650        ctx: &mut ExpressionContext<'source, '_, '_>,
2651    ) -> Result<Handle<crate::Expression>, Error<'source>> {
2652        let mut args = ctx.prepare_args(arguments, 2, span);
2653
2654        let argument = self.expression(args.next()?, ctx)?;
2655
2656        use SubgroupGather as Sg;
2657        let mode = if let Sg::BroadcastFirst = mode {
2658            crate::GatherMode::BroadcastFirst
2659        } else {
2660            let index = self.expression(args.next()?, ctx)?;
2661            match mode {
2662                Sg::Broadcast => crate::GatherMode::Broadcast(index),
2663                Sg::Shuffle => crate::GatherMode::Shuffle(index),
2664                Sg::ShuffleDown => crate::GatherMode::ShuffleDown(index),
2665                Sg::ShuffleUp => crate::GatherMode::ShuffleUp(index),
2666                Sg::ShuffleXor => crate::GatherMode::ShuffleXor(index),
2667                Sg::BroadcastFirst => unreachable!(),
2668            }
2669        };
2670
2671        args.finish()?;
2672
2673        let ty = ctx.register_type(argument)?;
2674
2675        let result =
2676            ctx.interrupt_emitter(crate::Expression::SubgroupOperationResult { ty }, span)?;
2677        let rctx = ctx.runtime_expression_ctx(span)?;
2678        rctx.block.push(
2679            crate::Statement::SubgroupGather {
2680                mode,
2681                argument,
2682                result,
2683            },
2684            span,
2685        );
2686        Ok(result)
2687    }
2688
2689    fn r#struct(
2690        &mut self,
2691        s: &ast::Struct<'source>,
2692        span: Span,
2693        ctx: &mut GlobalContext<'source, '_, '_>,
2694    ) -> Result<Handle<crate::Type>, Error<'source>> {
2695        let mut offset = 0;
2696        let mut struct_alignment = Alignment::ONE;
2697        let mut members = Vec::with_capacity(s.members.len());
2698
2699        for member in s.members.iter() {
2700            let ty = self.resolve_ast_type(member.ty, ctx)?;
2701
2702            self.layouter.update(ctx.module.to_ctx()).unwrap();
2703
2704            let member_min_size = self.layouter[ty].size;
2705            let member_min_alignment = self.layouter[ty].alignment;
2706
2707            let member_size = if let Some(size_expr) = member.size {
2708                let (size, span) = self.const_u32(size_expr, &mut ctx.as_const())?;
2709                if size < member_min_size {
2710                    return Err(Error::SizeAttributeTooLow(span, member_min_size));
2711                } else {
2712                    size
2713                }
2714            } else {
2715                member_min_size
2716            };
2717
2718            let member_alignment = if let Some(align_expr) = member.align {
2719                let (align, span) = self.const_u32(align_expr, &mut ctx.as_const())?;
2720                if let Some(alignment) = Alignment::new(align) {
2721                    if alignment < member_min_alignment {
2722                        return Err(Error::AlignAttributeTooLow(span, member_min_alignment));
2723                    } else {
2724                        alignment
2725                    }
2726                } else {
2727                    return Err(Error::NonPowerOfTwoAlignAttribute(span));
2728                }
2729            } else {
2730                member_min_alignment
2731            };
2732
2733            let binding = self.binding(&member.binding, ty, ctx)?;
2734
2735            offset = member_alignment.round_up(offset);
2736            struct_alignment = struct_alignment.max(member_alignment);
2737
2738            members.push(crate::StructMember {
2739                name: Some(member.name.name.to_owned()),
2740                ty,
2741                binding,
2742                offset,
2743            });
2744
2745            offset += member_size;
2746        }
2747
2748        let size = struct_alignment.round_up(offset);
2749        let inner = crate::TypeInner::Struct {
2750            members,
2751            span: size,
2752        };
2753
2754        let handle = ctx.module.types.insert(
2755            crate::Type {
2756                name: Some(s.name.name.to_string()),
2757                inner,
2758            },
2759            span,
2760        );
2761        Ok(handle)
2762    }
2763
2764    fn const_u32(
2765        &mut self,
2766        expr: Handle<ast::Expression<'source>>,
2767        ctx: &mut ExpressionContext<'source, '_, '_>,
2768    ) -> Result<(u32, Span), Error<'source>> {
2769        let span = ctx.ast_expressions.get_span(expr);
2770        let expr = self.expression(expr, ctx)?;
2771        let value = ctx
2772            .module
2773            .to_ctx()
2774            .eval_expr_to_u32(expr)
2775            .map_err(|err| match err {
2776                crate::proc::U32EvalError::NonConst => {
2777                    Error::ExpectedConstExprConcreteIntegerScalar(span)
2778                }
2779                crate::proc::U32EvalError::Negative => Error::ExpectedNonNegative(span),
2780            })?;
2781        Ok((value, span))
2782    }
2783
2784    fn array_size(
2785        &mut self,
2786        size: ast::ArraySize<'source>,
2787        ctx: &mut GlobalContext<'source, '_, '_>,
2788    ) -> Result<crate::ArraySize, Error<'source>> {
2789        Ok(match size {
2790            ast::ArraySize::Constant(expr) => {
2791                let span = ctx.ast_expressions.get_span(expr);
2792                let const_expr = self.expression(expr, &mut ctx.as_const())?;
2793                let len =
2794                    ctx.module
2795                        .to_ctx()
2796                        .eval_expr_to_u32(const_expr)
2797                        .map_err(|err| match err {
2798                            crate::proc::U32EvalError::NonConst => {
2799                                Error::ExpectedConstExprConcreteIntegerScalar(span)
2800                            }
2801                            crate::proc::U32EvalError::Negative => {
2802                                Error::ExpectedPositiveArrayLength(span)
2803                            }
2804                        })?;
2805                let size = NonZeroU32::new(len).ok_or(Error::ExpectedPositiveArrayLength(span))?;
2806                crate::ArraySize::Constant(size)
2807            }
2808            ast::ArraySize::Dynamic => crate::ArraySize::Dynamic,
2809        })
2810    }
2811
2812    /// Build the Naga equivalent of a named AST type.
2813    ///
2814    /// Return a Naga `Handle<Type>` representing the front-end type
2815    /// `handle`, which should be named `name`, if given.
2816    ///
2817    /// If `handle` refers to a type cached in [`SpecialTypes`],
2818    /// `name` may be ignored.
2819    ///
2820    /// [`SpecialTypes`]: crate::SpecialTypes
2821    fn resolve_named_ast_type(
2822        &mut self,
2823        handle: Handle<ast::Type<'source>>,
2824        name: Option<String>,
2825        ctx: &mut GlobalContext<'source, '_, '_>,
2826    ) -> Result<Handle<crate::Type>, Error<'source>> {
2827        let inner = match ctx.types[handle] {
2828            ast::Type::Scalar(scalar) => scalar.to_inner_scalar(),
2829            ast::Type::Vector { size, scalar } => scalar.to_inner_vector(size),
2830            ast::Type::Matrix {
2831                rows,
2832                columns,
2833                width,
2834            } => crate::TypeInner::Matrix {
2835                columns,
2836                rows,
2837                scalar: crate::Scalar::float(width),
2838            },
2839            ast::Type::Atomic(scalar) => scalar.to_inner_atomic(),
2840            ast::Type::Pointer { base, space } => {
2841                let base = self.resolve_ast_type(base, ctx)?;
2842                crate::TypeInner::Pointer { base, space }
2843            }
2844            ast::Type::Array { base, size } => {
2845                let base = self.resolve_ast_type(base, ctx)?;
2846                let size = self.array_size(size, ctx)?;
2847
2848                self.layouter.update(ctx.module.to_ctx()).unwrap();
2849                let stride = self.layouter[base].to_stride();
2850
2851                crate::TypeInner::Array { base, size, stride }
2852            }
2853            ast::Type::Image {
2854                dim,
2855                arrayed,
2856                class,
2857            } => crate::TypeInner::Image {
2858                dim,
2859                arrayed,
2860                class,
2861            },
2862            ast::Type::Sampler { comparison } => crate::TypeInner::Sampler { comparison },
2863            ast::Type::AccelerationStructure => crate::TypeInner::AccelerationStructure,
2864            ast::Type::RayQuery => crate::TypeInner::RayQuery,
2865            ast::Type::BindingArray { base, size } => {
2866                let base = self.resolve_ast_type(base, ctx)?;
2867                let size = self.array_size(size, ctx)?;
2868                crate::TypeInner::BindingArray { base, size }
2869            }
2870            ast::Type::RayDesc => {
2871                return Ok(ctx.module.generate_ray_desc_type());
2872            }
2873            ast::Type::RayIntersection => {
2874                return Ok(ctx.module.generate_ray_intersection_type());
2875            }
2876            ast::Type::User(ref ident) => {
2877                return match ctx.globals.get(ident.name) {
2878                    Some(&LoweredGlobalDecl::Type(handle)) => Ok(handle),
2879                    Some(_) => Err(Error::Unexpected(ident.span, ExpectedToken::Type)),
2880                    None => Err(Error::UnknownType(ident.span)),
2881                }
2882            }
2883        };
2884
2885        Ok(ctx.ensure_type_exists(name, inner))
2886    }
2887
2888    /// Return a Naga `Handle<Type>` representing the front-end type `handle`.
2889    fn resolve_ast_type(
2890        &mut self,
2891        handle: Handle<ast::Type<'source>>,
2892        ctx: &mut GlobalContext<'source, '_, '_>,
2893    ) -> Result<Handle<crate::Type>, Error<'source>> {
2894        self.resolve_named_ast_type(handle, None, ctx)
2895    }
2896
2897    fn binding(
2898        &mut self,
2899        binding: &Option<ast::Binding<'source>>,
2900        ty: Handle<crate::Type>,
2901        ctx: &mut GlobalContext<'source, '_, '_>,
2902    ) -> Result<Option<crate::Binding>, Error<'source>> {
2903        Ok(match *binding {
2904            Some(ast::Binding::BuiltIn(b)) => Some(crate::Binding::BuiltIn(b)),
2905            Some(ast::Binding::Location {
2906                location,
2907                second_blend_source,
2908                interpolation,
2909                sampling,
2910            }) => {
2911                let mut binding = crate::Binding::Location {
2912                    location: self.const_u32(location, &mut ctx.as_const())?.0,
2913                    second_blend_source,
2914                    interpolation,
2915                    sampling,
2916                };
2917                binding.apply_default_interpolation(&ctx.module.types[ty].inner);
2918                Some(binding)
2919            }
2920            None => None,
2921        })
2922    }
2923
2924    fn ray_query_pointer(
2925        &mut self,
2926        expr: Handle<ast::Expression<'source>>,
2927        ctx: &mut ExpressionContext<'source, '_, '_>,
2928    ) -> Result<Handle<crate::Expression>, Error<'source>> {
2929        let span = ctx.ast_expressions.get_span(expr);
2930        let pointer = self.expression(expr, ctx)?;
2931
2932        match *resolve_inner!(ctx, pointer) {
2933            crate::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner {
2934                crate::TypeInner::RayQuery => Ok(pointer),
2935                ref other => {
2936                    log::error!("Pointer type to {:?} passed to ray query op", other);
2937                    Err(Error::InvalidRayQueryPointer(span))
2938                }
2939            },
2940            ref other => {
2941                log::error!("Type {:?} passed to ray query op", other);
2942                Err(Error::InvalidRayQueryPointer(span))
2943            }
2944        }
2945    }
2946}
2947
2948impl crate::AtomicFunction {
2949    pub fn map(word: &str) -> Option<Self> {
2950        Some(match word {
2951            "atomicAdd" => crate::AtomicFunction::Add,
2952            "atomicSub" => crate::AtomicFunction::Subtract,
2953            "atomicAnd" => crate::AtomicFunction::And,
2954            "atomicOr" => crate::AtomicFunction::InclusiveOr,
2955            "atomicXor" => crate::AtomicFunction::ExclusiveOr,
2956            "atomicMin" => crate::AtomicFunction::Min,
2957            "atomicMax" => crate::AtomicFunction::Max,
2958            "atomicExchange" => crate::AtomicFunction::Exchange { compare: None },
2959            _ => return None,
2960        })
2961    }
2962}