Coverage Report

Created: 2025-09-27 07:05

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