Skip to main content

naga/valid/
function.rs

1use alloc::{format, string::String};
2
3use super::{
4    analyzer::{UniformityDisruptor, UniformityRequirements},
5    ExpressionError, FunctionInfo, ModuleInfo,
6};
7use crate::arena::{Arena, UniqueArena};
8use crate::arena::{Handle, HandleSet};
9use crate::proc::TypeResolution;
10use crate::span::WithSpan;
11use crate::span::{AddSpan as _, MapErrWithSpan as _};
12
13#[derive(Clone, Debug, thiserror::Error)]
14#[cfg_attr(test, derive(PartialEq))]
15pub enum CallError {
16    #[error("Argument {index} expression is invalid")]
17    Argument {
18        index: usize,
19        source: ExpressionError,
20    },
21    #[error("Result expression {0:?} has already been introduced earlier")]
22    ResultAlreadyInScope(Handle<crate::Expression>),
23    #[error("Result expression {0:?} is populated by multiple `Call` statements")]
24    ResultAlreadyPopulated(Handle<crate::Expression>),
25    #[error("Requires {required} arguments, but {seen} are provided")]
26    ArgumentCount { required: usize, seen: usize },
27    #[error("Argument {index} value {seen_expression:?} doesn't match the type {required:?}")]
28    ArgumentType {
29        index: usize,
30        required: Handle<crate::Type>,
31        seen_expression: Handle<crate::Expression>,
32    },
33    #[error("The emitted expression doesn't match the call")]
34    ExpressionMismatch(Option<Handle<crate::Expression>>),
35}
36
37#[derive(Clone, Debug, thiserror::Error)]
38#[cfg_attr(test, derive(PartialEq))]
39pub enum AtomicError {
40    #[error("Pointer {0:?} to atomic is invalid.")]
41    InvalidPointer(Handle<crate::Expression>),
42    #[error("Address space {0:?} is not supported.")]
43    InvalidAddressSpace(crate::AddressSpace),
44    #[error("Operand {0:?} has invalid type.")]
45    InvalidOperand(Handle<crate::Expression>),
46    #[error("Operator {0:?} is not supported.")]
47    InvalidOperator(crate::AtomicFunction),
48    #[error("Result expression {0:?} is not an `AtomicResult` expression")]
49    InvalidResultExpression(Handle<crate::Expression>),
50    #[error("Result expression {0:?} is marked as an `exchange`")]
51    ResultExpressionExchange(Handle<crate::Expression>),
52    #[error("Result expression {0:?} is not marked as an `exchange`")]
53    ResultExpressionNotExchange(Handle<crate::Expression>),
54    #[error("Result type for {0:?} doesn't match the statement")]
55    ResultTypeMismatch(Handle<crate::Expression>),
56    #[error("Exchange operations must return a value")]
57    MissingReturnValue,
58    #[error("Capability {0:?} is required")]
59    MissingCapability(super::Capabilities),
60    #[error("Result expression {0:?} is populated by multiple `Atomic` statements")]
61    ResultAlreadyPopulated(Handle<crate::Expression>),
62}
63
64#[derive(Clone, Debug, thiserror::Error)]
65#[cfg_attr(test, derive(PartialEq))]
66pub enum SubgroupError {
67    #[error("Operand {0:?} has invalid type.")]
68    InvalidOperand(Handle<crate::Expression>),
69    #[error("Result type for {0:?} doesn't match the statement")]
70    ResultTypeMismatch(Handle<crate::Expression>),
71    #[error("Support for subgroup operation {0:?} is required")]
72    UnsupportedOperation(super::SubgroupOperationSet),
73    #[error("Unknown operation")]
74    UnknownOperation,
75    #[error("Invocation ID must be a const-expression")]
76    InvalidInvocationIdExprType(Handle<crate::Expression>),
77}
78
79#[derive(Clone, Debug, thiserror::Error)]
80#[cfg_attr(test, derive(PartialEq))]
81pub enum LocalVariableError {
82    #[error("Local variable has a type {0:?} that can't be stored in a local variable.")]
83    InvalidType(Handle<crate::Type>),
84    #[error("Initializer doesn't match the variable type")]
85    InitializerType,
86    #[error("Initializer is not a const or override expression")]
87    NonConstOrOverrideInitializer,
88    #[error("Local variable has a type `ray_query` and so cannot be initialized.")]
89    RayQueryWithInitializeExpression,
90}
91
92#[derive(Clone, Debug, thiserror::Error)]
93#[cfg_attr(test, derive(PartialEq))]
94pub enum FunctionError {
95    #[error("Expression {handle:?} is invalid")]
96    Expression {
97        handle: Handle<crate::Expression>,
98        source: ExpressionError,
99    },
100    #[error("Expression {0:?} can't be introduced - it's already in scope")]
101    ExpressionAlreadyInScope(Handle<crate::Expression>),
102    #[error("Local variable {handle:?} '{name}' is invalid")]
103    LocalVariable {
104        handle: Handle<crate::LocalVariable>,
105        name: String,
106        source: LocalVariableError,
107    },
108    #[error("Argument '{name}' at index {index} has a type that can't be passed into functions.")]
109    InvalidArgumentType { index: usize, name: String },
110    #[error("The function's given return type cannot be returned from functions")]
111    NonConstructibleReturnType,
112    #[error("Argument '{name}' at index {index} is a pointer of space {space:?}, which can't be passed into functions.")]
113    InvalidArgumentPointerSpace {
114        index: usize,
115        name: String,
116        space: crate::AddressSpace,
117    },
118    #[error("The `break` is used outside of a `loop` or `switch` context")]
119    BreakOutsideOfLoopOrSwitch,
120    #[error("The `continue` is used outside of a `loop` context")]
121    ContinueOutsideOfLoop,
122    #[error("The `return` is called within a `continuing` block")]
123    InvalidReturnSpot,
124    #[error("The `return` expression {expression:?} does not match the declared return type {expected_ty:?}")]
125    InvalidReturnType {
126        expression: Option<Handle<crate::Expression>>,
127        expected_ty: Option<Handle<crate::Type>>,
128    },
129    #[error("The `if` condition {0:?} is not a boolean scalar")]
130    InvalidIfType(Handle<crate::Expression>),
131    #[error("The `switch` value {0:?} is not an integer scalar")]
132    InvalidSwitchType(Handle<crate::Expression>),
133    #[error("Multiple `switch` cases for {0:?} are present")]
134    ConflictingSwitchCase(crate::SwitchValue),
135    #[error("The `switch` contains cases with conflicting types")]
136    ConflictingCaseType,
137    #[error("The `switch` is missing a `default` case")]
138    MissingDefaultCase,
139    #[error("Multiple `default` cases are present")]
140    MultipleDefaultCases,
141    #[error("The last `switch` case contains a `fallthrough`")]
142    LastCaseFallTrough,
143    #[error("The pointer {0:?} doesn't relate to a valid destination for a store")]
144    InvalidStorePointer(Handle<crate::Expression>),
145    #[error("Image store texture parameter type mismatch")]
146    InvalidStoreTexture {
147        actual: Handle<crate::Expression>,
148        actual_ty: crate::TypeInner,
149    },
150    #[error("Image store value parameter type mismatch")]
151    InvalidStoreValue {
152        actual: Handle<crate::Expression>,
153        actual_ty: crate::TypeInner,
154        expected_ty: crate::TypeInner,
155    },
156    #[error("The type of {value:?} doesn't match the type stored in {pointer:?}")]
157    InvalidStoreTypes {
158        pointer: Handle<crate::Expression>,
159        value: Handle<crate::Expression>,
160    },
161    #[error("Image store parameters are invalid")]
162    InvalidImageStore(#[source] ExpressionError),
163    #[error("Image atomic parameters are invalid")]
164    InvalidImageAtomic(#[source] ExpressionError),
165    #[error("Image atomic function is invalid")]
166    InvalidImageAtomicFunction(crate::AtomicFunction),
167    #[error("Image atomic value is invalid")]
168    InvalidImageAtomicValue(Handle<crate::Expression>),
169    #[error("Call to {function:?} is invalid")]
170    InvalidCall {
171        function: Handle<crate::Function>,
172        #[source]
173        error: CallError,
174    },
175    #[error("Atomic operation is invalid")]
176    InvalidAtomic(#[from] AtomicError),
177    #[error("Ray Query {0:?} is not a local variable")]
178    InvalidRayQueryExpression(Handle<crate::Expression>),
179    #[error("Acceleration structure {0:?} is not a matching expression")]
180    InvalidAccelerationStructure(Handle<crate::Expression>),
181    #[error(
182        "Acceleration structure {0:?} is missing flag vertex_return while Ray Query {1:?} does"
183    )]
184    MissingAccelerationStructureVertexReturn(Handle<crate::Expression>, Handle<crate::Expression>),
185    #[error("Ray Query {0:?} is missing flag vertex_return")]
186    MissingRayQueryVertexReturn(Handle<crate::Expression>),
187    #[error("Ray descriptor {0:?} is not a matching expression")]
188    InvalidRayDescriptor(Handle<crate::Expression>),
189    #[error("Ray Query {0:?} does not have a matching type")]
190    InvalidRayQueryType(Handle<crate::Type>),
191    #[error("Hit distance {0:?} must be an f32")]
192    InvalidHitDistanceType(Handle<crate::Expression>),
193    #[error("Shader requires capability {0:?}")]
194    MissingCapability(super::Capabilities),
195    #[error(
196        "Required uniformity of control flow for {0:?} in {1:?} is not fulfilled because of {2:?}"
197    )]
198    NonUniformControlFlow(
199        UniformityRequirements,
200        Handle<crate::Expression>,
201        UniformityDisruptor,
202    ),
203    #[error("Functions that are not entry points cannot have `@location` or `@builtin` attributes on their arguments: \"{name}\" has attributes")]
204    PipelineInputRegularFunction { name: String },
205    #[error("Functions that are not entry points cannot have `@location` or `@builtin` attributes on their return value types")]
206    PipelineOutputRegularFunction,
207    #[error("Required uniformity for WorkGroupUniformLoad is not fulfilled because of {0:?}")]
208    // The actual load statement will be "pointed to" by the span
209    NonUniformWorkgroupUniformLoad(UniformityDisruptor),
210    // This is only possible with a misbehaving frontend
211    #[error("The expression {0:?} for a WorkGroupUniformLoad isn't a WorkgroupUniformLoadResult")]
212    WorkgroupUniformLoadExpressionMismatch(Handle<crate::Expression>),
213    #[error("The expression {0:?} is not valid as a WorkGroupUniformLoad argument. It should be a Pointer in Workgroup address space")]
214    WorkgroupUniformLoadInvalidPointer(Handle<crate::Expression>),
215    #[error("Subgroup operation is invalid")]
216    InvalidSubgroup(#[from] SubgroupError),
217    #[error("Invalid target type for a cooperative store")]
218    InvalidCooperativeStoreTarget(Handle<crate::Expression>),
219    #[error("Cooperative load/store data pointer has invalid type")]
220    InvalidCooperativeDataPointer(Handle<crate::Expression>),
221    #[error("Emit statement should not cover \"result\" expressions like {0:?}")]
222    EmitResult(Handle<crate::Expression>),
223    #[error("Expression not visited by the appropriate statement")]
224    UnvisitedExpression(Handle<crate::Expression>),
225    #[error("Expression {0:?} in mesh shader intrinsic call should be `u32` (is the expression a signed integer?)")]
226    InvalidMeshFunctionCall(Handle<crate::Expression>),
227    #[error("Mesh output types differ from {0:?} to {1:?}")]
228    ConflictingMeshOutputTypes(Handle<crate::Expression>, Handle<crate::Expression>),
229    #[error("Task payload variables differ from {0:?} to {1:?}")]
230    ConflictingTaskPayloadVariables(Handle<crate::Expression>, Handle<crate::Expression>),
231    #[error("Mesh shader output at {0:?} is not a user-defined struct")]
232    InvalidMeshShaderOutputType(Handle<crate::Expression>),
233    #[error("The payload type passed to `traceRay` must be a pointer")]
234    InvalidPayloadType,
235    #[error("The payload type passed to `traceRay` must be a pointer with an address space of `ray_payload` or `incoming_ray_payload`, instead got {0:?}")]
236    InvalidPayloadAddressSpace(crate::AddressSpace),
237    #[error("The payload type ({0:?}) passed to `traceRay` does not match the previous one {1:?}")]
238    MismatchedPayloadType(Handle<crate::Type>, Handle<crate::Type>),
239    #[error("The payload passed to `traceRay` must be a pointer directly to a global variable")]
240    PayloadPointerNotGlobal,
241    #[error("Argument {0:?} for `debugPrintf` must be a supported scalar type")]
242    InvalidDebugPrintfArgument(Handle<crate::Expression>),
243    #[error("Tried to store to pointer {0:?} which is a ray query and so cannot be assigned to")]
244    RayQueryStore(Handle<crate::Expression>),
245}
246
247bitflags::bitflags! {
248    #[repr(transparent)]
249    #[derive(Clone, Copy)]
250    struct ControlFlowAbility: u8 {
251        /// The control can return out of this block.
252        const RETURN = 0x1;
253        /// The control can break.
254        const BREAK = 0x2;
255        /// The control can continue.
256        const CONTINUE = 0x4;
257    }
258}
259
260struct BlockInfo {
261    stages: super::ShaderStages,
262}
263
264struct BlockContext<'a> {
265    abilities: ControlFlowAbility,
266    info: &'a FunctionInfo,
267    expressions: &'a Arena<crate::Expression>,
268    types: &'a UniqueArena<crate::Type>,
269    local_vars: &'a Arena<crate::LocalVariable>,
270    global_vars: &'a Arena<crate::GlobalVariable>,
271    functions: &'a Arena<crate::Function>,
272    special_types: &'a crate::SpecialTypes,
273    prev_infos: &'a [FunctionInfo],
274    return_type: Option<Handle<crate::Type>>,
275    local_expr_kind: &'a crate::proc::ExpressionKindTracker,
276}
277
278impl<'a> BlockContext<'a> {
279    fn new(
280        fun: &'a crate::Function,
281        module: &'a crate::Module,
282        info: &'a FunctionInfo,
283        prev_infos: &'a [FunctionInfo],
284        local_expr_kind: &'a crate::proc::ExpressionKindTracker,
285    ) -> Self {
286        Self {
287            abilities: ControlFlowAbility::RETURN,
288            info,
289            expressions: &fun.expressions,
290            types: &module.types,
291            local_vars: &fun.local_variables,
292            global_vars: &module.global_variables,
293            functions: &module.functions,
294            special_types: &module.special_types,
295            prev_infos,
296            return_type: fun.result.as_ref().map(|fr| fr.ty),
297            local_expr_kind,
298        }
299    }
300
301    const fn with_abilities(&self, abilities: ControlFlowAbility) -> Self {
302        BlockContext { abilities, ..*self }
303    }
304
305    fn get_expression(&self, handle: Handle<crate::Expression>) -> &'a crate::Expression {
306        &self.expressions[handle]
307    }
308
309    fn resolve_type_impl(
310        &self,
311        handle: Handle<crate::Expression>,
312        valid_expressions: &HandleSet<crate::Expression>,
313    ) -> Result<&TypeResolution, WithSpan<ExpressionError>> {
314        if !valid_expressions.contains(handle) {
315            Err(ExpressionError::NotInScope.with_span_handle(handle, self.expressions))
316        } else {
317            Ok(&self.info[handle].ty)
318        }
319    }
320
321    fn resolve_type(
322        &self,
323        handle: Handle<crate::Expression>,
324        valid_expressions: &HandleSet<crate::Expression>,
325    ) -> Result<&TypeResolution, WithSpan<FunctionError>> {
326        self.resolve_type_impl(handle, valid_expressions)
327            .map_err_inner(|source| FunctionError::Expression { handle, source }.with_span())
328    }
329
330    fn resolve_type_inner(
331        &self,
332        handle: Handle<crate::Expression>,
333        valid_expressions: &HandleSet<crate::Expression>,
334    ) -> Result<&crate::TypeInner, WithSpan<FunctionError>> {
335        self.resolve_type(handle, valid_expressions)
336            .map(|tr| tr.inner_with(self.types))
337    }
338
339    fn resolve_pointer_type(&self, handle: Handle<crate::Expression>) -> &crate::TypeInner {
340        self.info[handle].ty.inner_with(self.types)
341    }
342
343    fn compare_types(&self, lhs: &TypeResolution, rhs: &TypeResolution) -> bool {
344        crate::proc::compare_types(lhs, rhs, self.types)
345    }
346}
347
348impl super::Validator {
349    fn validate_call(
350        &mut self,
351        function: Handle<crate::Function>,
352        arguments: &[Handle<crate::Expression>],
353        result: Option<Handle<crate::Expression>>,
354        context: &BlockContext,
355    ) -> Result<super::ShaderStages, WithSpan<CallError>> {
356        let fun = &context.functions[function];
357        if fun.arguments.len() != arguments.len() {
358            return Err(CallError::ArgumentCount {
359                required: fun.arguments.len(),
360                seen: arguments.len(),
361            }
362            .with_span());
363        }
364        for (index, (arg, &expr)) in fun.arguments.iter().zip(arguments).enumerate() {
365            let ty = context
366                .resolve_type_impl(expr, &self.valid_expression_set)
367                .map_err_inner(|source| {
368                    CallError::Argument { index, source }
369                        .with_span_handle(expr, context.expressions)
370                })?;
371            if !context.compare_types(&TypeResolution::Handle(arg.ty), ty) {
372                return Err(CallError::ArgumentType {
373                    index,
374                    required: arg.ty,
375                    seen_expression: expr,
376                }
377                .with_span_handle(expr, context.expressions));
378            }
379        }
380
381        if let Some(expr) = result {
382            if self.valid_expression_set.insert(expr) {
383                self.valid_expression_list.push(expr);
384            } else {
385                return Err(CallError::ResultAlreadyInScope(expr)
386                    .with_span_handle(expr, context.expressions));
387            }
388            match context.expressions[expr] {
389                crate::Expression::CallResult(callee)
390                    if fun.result.is_some() && callee == function =>
391                {
392                    if !self.needs_visit.remove(expr) {
393                        return Err(CallError::ResultAlreadyPopulated(expr)
394                            .with_span_handle(expr, context.expressions));
395                    }
396                }
397                _ => {
398                    return Err(CallError::ExpressionMismatch(result)
399                        .with_span_handle(expr, context.expressions))
400                }
401            }
402        } else if fun.result.is_some() {
403            return Err(CallError::ExpressionMismatch(result).with_span());
404        }
405
406        let callee_info = &context.prev_infos[function.index()];
407        Ok(callee_info.available_stages)
408    }
409
410    fn emit_expression(
411        &mut self,
412        handle: Handle<crate::Expression>,
413        context: &BlockContext,
414    ) -> Result<(), WithSpan<FunctionError>> {
415        if self.valid_expression_set.insert(handle) {
416            self.valid_expression_list.push(handle);
417            Ok(())
418        } else {
419            Err(FunctionError::ExpressionAlreadyInScope(handle)
420                .with_span_handle(handle, context.expressions))
421        }
422    }
423
424    fn validate_atomic(
425        &mut self,
426        pointer: Handle<crate::Expression>,
427        fun: &crate::AtomicFunction,
428        value: Handle<crate::Expression>,
429        result: Option<Handle<crate::Expression>>,
430        span: crate::Span,
431        context: &BlockContext,
432    ) -> Result<(), WithSpan<FunctionError>> {
433        // The `pointer` operand must be a pointer to an atomic value.
434        let pointer_inner = context.resolve_type_inner(pointer, &self.valid_expression_set)?;
435        let crate::TypeInner::Pointer {
436            base: pointer_base,
437            space: pointer_space,
438        } = *pointer_inner
439        else {
440            log::error!("Atomic operation on type {:?}", *pointer_inner);
441            return Err(AtomicError::InvalidPointer(pointer)
442                .with_span_handle(pointer, context.expressions)
443                .into_other());
444        };
445        let crate::TypeInner::Atomic(pointer_scalar) = context.types[pointer_base].inner else {
446            log::error!(
447                "Atomic pointer to type {:?}",
448                context.types[pointer_base].inner
449            );
450            return Err(AtomicError::InvalidPointer(pointer)
451                .with_span_handle(pointer, context.expressions)
452                .into_other());
453        };
454
455        // The `value` operand must be a scalar of the same type as the atomic.
456        let value_inner = context.resolve_type_inner(value, &self.valid_expression_set)?;
457        let crate::TypeInner::Scalar(value_scalar) = *value_inner else {
458            log::error!("Atomic operand type {:?}", *value_inner);
459            return Err(AtomicError::InvalidOperand(value)
460                .with_span_handle(value, context.expressions)
461                .into_other());
462        };
463        if pointer_scalar != value_scalar {
464            log::error!("Atomic operand type {:?}", *value_inner);
465            return Err(AtomicError::InvalidOperand(value)
466                .with_span_handle(value, context.expressions)
467                .into_other());
468        }
469
470        match pointer_scalar {
471            // Check for the special restrictions on 64-bit atomic operations.
472            //
473            // We don't need to consider other widths here: this function has already checked
474            // that `pointer`'s type is an `Atomic`, and `validate_type` has already checked
475            // that `Atomic` type has a permitted scalar width.
476            crate::Scalar::I64 | crate::Scalar::U64 => {
477                // `Capabilities::SHADER_INT64_ATOMIC_ALL_OPS` enables all sorts of 64-bit
478                // atomic operations.
479                if self
480                    .capabilities
481                    .contains(super::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS)
482                {
483                    // okay
484                } else {
485                    // `Capabilities::SHADER_INT64_ATOMIC_MIN_MAX` allows `Min` and
486                    // `Max` on operations in `Storage`, without a return value.
487                    if matches!(
488                        *fun,
489                        crate::AtomicFunction::Min | crate::AtomicFunction::Max
490                    ) && matches!(pointer_space, crate::AddressSpace::Storage { .. })
491                        && result.is_none()
492                    {
493                        if !self
494                            .capabilities
495                            .contains(super::Capabilities::SHADER_INT64_ATOMIC_MIN_MAX)
496                        {
497                            log::error!("Int64 min-max atomic operations are not supported");
498                            return Err(AtomicError::MissingCapability(
499                                super::Capabilities::SHADER_INT64_ATOMIC_MIN_MAX,
500                            )
501                            .with_span_handle(value, context.expressions)
502                            .into_other());
503                        }
504                    } else {
505                        // Otherwise, we require the full 64-bit atomic capability.
506                        log::error!("Int64 atomic operations are not supported");
507                        return Err(AtomicError::MissingCapability(
508                            super::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS,
509                        )
510                        .with_span_handle(value, context.expressions)
511                        .into_other());
512                    }
513                }
514            }
515            // Check for the special restrictions on 32-bit floating-point atomic operations.
516            crate::Scalar::F32 => {
517                // `Capabilities::SHADER_FLOAT32_ATOMIC` allows 32-bit floating-point
518                // atomic operations `Add`, `Subtract`, and `Exchange`
519                // in the `Storage` address space.
520                if !self
521                    .capabilities
522                    .contains(super::Capabilities::SHADER_FLOAT32_ATOMIC)
523                {
524                    log::error!("Float32 atomic operations are not supported");
525                    return Err(AtomicError::MissingCapability(
526                        super::Capabilities::SHADER_FLOAT32_ATOMIC,
527                    )
528                    .with_span_handle(value, context.expressions)
529                    .into_other());
530                }
531                if !matches!(
532                    *fun,
533                    crate::AtomicFunction::Add
534                        | crate::AtomicFunction::Subtract
535                        | crate::AtomicFunction::Exchange { compare: None }
536                ) {
537                    log::error!("Float32 atomic operation {fun:?} is not supported");
538                    return Err(AtomicError::InvalidOperator(*fun)
539                        .with_span_handle(value, context.expressions)
540                        .into_other());
541                }
542                if !matches!(pointer_space, crate::AddressSpace::Storage { .. }) {
543                    log::error!(
544                        "Float32 atomic operations are only supported in the Storage address space"
545                    );
546                    return Err(AtomicError::InvalidAddressSpace(pointer_space)
547                        .with_span_handle(value, context.expressions)
548                        .into_other());
549                }
550            }
551            _ => {}
552        }
553
554        // The result expression must be appropriate to the operation.
555        match result {
556            Some(result) => {
557                // The `result` handle must refer to an `AtomicResult` expression.
558                let crate::Expression::AtomicResult {
559                    ty: result_ty,
560                    comparison,
561                } = context.expressions[result]
562                else {
563                    return Err(AtomicError::InvalidResultExpression(result)
564                        .with_span_handle(result, context.expressions)
565                        .into_other());
566                };
567
568                // Note that this expression has been visited by the proper kind
569                // of statement.
570                if !self.needs_visit.remove(result) {
571                    return Err(AtomicError::ResultAlreadyPopulated(result)
572                        .with_span_handle(result, context.expressions)
573                        .into_other());
574                }
575
576                // The constraints on the result type depend on the atomic function.
577                if let crate::AtomicFunction::Exchange {
578                    compare: Some(compare),
579                } = *fun
580                {
581                    // The comparison value must be a scalar of the same type as the
582                    // atomic we're operating on.
583                    let compare_inner =
584                        context.resolve_type_inner(compare, &self.valid_expression_set)?;
585                    if !compare_inner.non_struct_equivalent(value_inner, context.types) {
586                        log::error!(
587                            "Atomic exchange comparison has a different type from the value"
588                        );
589                        return Err(AtomicError::InvalidOperand(compare)
590                            .with_span_handle(compare, context.expressions)
591                            .into_other());
592                    }
593
594                    // The result expression must be an `__atomic_compare_exchange_result`
595                    // struct whose `old_value` member is of the same type as the atomic
596                    // we're operating on.
597                    let crate::TypeInner::Struct { ref members, .. } =
598                        context.types[result_ty].inner
599                    else {
600                        return Err(AtomicError::ResultTypeMismatch(result)
601                            .with_span_handle(result, context.expressions)
602                            .into_other());
603                    };
604                    if !super::validate_atomic_compare_exchange_struct(
605                        context.types,
606                        members,
607                        |ty: &crate::TypeInner| *ty == crate::TypeInner::Scalar(pointer_scalar),
608                    ) {
609                        return Err(AtomicError::ResultTypeMismatch(result)
610                            .with_span_handle(result, context.expressions)
611                            .into_other());
612                    }
613
614                    // The result expression must be for a comparison operation.
615                    if !comparison {
616                        return Err(AtomicError::ResultExpressionNotExchange(result)
617                            .with_span_handle(result, context.expressions)
618                            .into_other());
619                    }
620                } else {
621                    // The result expression must be a scalar of the same type as the
622                    // atomic we're operating on.
623                    let result_inner = &context.types[result_ty].inner;
624                    if !result_inner.non_struct_equivalent(value_inner, context.types) {
625                        return Err(AtomicError::ResultTypeMismatch(result)
626                            .with_span_handle(result, context.expressions)
627                            .into_other());
628                    }
629
630                    // The result expression must not be for a comparison.
631                    if comparison {
632                        return Err(AtomicError::ResultExpressionExchange(result)
633                            .with_span_handle(result, context.expressions)
634                            .into_other());
635                    }
636                }
637                self.emit_expression(result, context)?;
638            }
639
640            None => {
641                // Exchange operations must always produce a value.
642                if let crate::AtomicFunction::Exchange { compare: None } = *fun {
643                    log::error!("Atomic exchange's value is unused");
644                    return Err(AtomicError::MissingReturnValue
645                        .with_span_static(span, "atomic exchange operation")
646                        .into_other());
647                }
648            }
649        }
650
651        Ok(())
652    }
653    fn validate_subgroup_operation(
654        &mut self,
655        op: &crate::SubgroupOperation,
656        collective_op: &crate::CollectiveOperation,
657        argument: Handle<crate::Expression>,
658        result: Handle<crate::Expression>,
659        context: &BlockContext,
660    ) -> Result<(), WithSpan<FunctionError>> {
661        let argument_inner = context.resolve_type_inner(argument, &self.valid_expression_set)?;
662
663        let (is_scalar, scalar) = match *argument_inner {
664            crate::TypeInner::Scalar(scalar) => (true, scalar),
665            crate::TypeInner::Vector { scalar, .. } => (false, scalar),
666            _ => {
667                log::error!("Subgroup operand type {argument_inner:?}");
668                return Err(SubgroupError::InvalidOperand(argument)
669                    .with_span_handle(argument, context.expressions)
670                    .into_other());
671            }
672        };
673
674        use crate::ScalarKind as sk;
675        use crate::SubgroupOperation as sg;
676        match (scalar.kind, *op) {
677            (sk::Bool, sg::All | sg::Any) if is_scalar => {}
678            (sk::Sint | sk::Uint | sk::Float, sg::Add | sg::Mul | sg::Min | sg::Max) => {}
679            // Subgroup bitwise ops require >= 32-bit integers because HLSL's
680            // WaveActiveBitAnd/Or/Xor don't support 16-bit types.
681            (sk::Sint | sk::Uint, sg::And | sg::Or | sg::Xor) if scalar.width >= 4 => {}
682
683            (_, _) => {
684                log::error!("Subgroup operand type {argument_inner:?}");
685                return Err(SubgroupError::InvalidOperand(argument)
686                    .with_span_handle(argument, context.expressions)
687                    .into_other());
688            }
689        };
690
691        use crate::CollectiveOperation as co;
692        match (*collective_op, *op) {
693            (
694                co::Reduce,
695                sg::All
696                | sg::Any
697                | sg::Add
698                | sg::Mul
699                | sg::Min
700                | sg::Max
701                | sg::And
702                | sg::Or
703                | sg::Xor,
704            ) => {}
705            (co::InclusiveScan | co::ExclusiveScan, sg::Add | sg::Mul) => {}
706
707            (_, _) => {
708                return Err(SubgroupError::UnknownOperation.with_span().into_other());
709            }
710        };
711
712        self.emit_expression(result, context)?;
713        match context.expressions[result] {
714            crate::Expression::SubgroupOperationResult { ty }
715                if { &context.types[ty].inner == argument_inner } => {}
716            _ => {
717                return Err(SubgroupError::ResultTypeMismatch(result)
718                    .with_span_handle(result, context.expressions)
719                    .into_other())
720            }
721        }
722        Ok(())
723    }
724    fn validate_subgroup_gather(
725        &mut self,
726        mode: &crate::GatherMode,
727        argument: Handle<crate::Expression>,
728        result: Handle<crate::Expression>,
729        context: &BlockContext,
730    ) -> Result<(), WithSpan<FunctionError>> {
731        match *mode {
732            crate::GatherMode::BroadcastFirst => {}
733            crate::GatherMode::Broadcast(index)
734            | crate::GatherMode::Shuffle(index)
735            | crate::GatherMode::ShuffleDown(index)
736            | crate::GatherMode::ShuffleUp(index)
737            | crate::GatherMode::ShuffleXor(index)
738            | crate::GatherMode::QuadBroadcast(index) => {
739                let index_ty = context.resolve_type_inner(index, &self.valid_expression_set)?;
740                match *index_ty {
741                    crate::TypeInner::Scalar(crate::Scalar::U32) => {}
742                    _ => {
743                        log::error!(
744                            "Subgroup gather index type {index_ty:?}, expected unsigned int"
745                        );
746                        return Err(SubgroupError::InvalidOperand(argument)
747                            .with_span_handle(index, context.expressions)
748                            .into_other());
749                    }
750                }
751            }
752            crate::GatherMode::QuadSwap(_) => {}
753        }
754        match *mode {
755            crate::GatherMode::Broadcast(index) | crate::GatherMode::QuadBroadcast(index)
756                if !context.local_expr_kind.is_const(index) =>
757            {
758                return Err(SubgroupError::InvalidInvocationIdExprType(index)
759                    .with_span_handle(index, context.expressions)
760                    .into_other());
761            }
762            _ => {}
763        }
764        let argument_inner = context.resolve_type_inner(argument, &self.valid_expression_set)?;
765        if !matches!(*argument_inner,
766            crate::TypeInner::Scalar ( scalar, .. ) | crate::TypeInner::Vector { scalar, .. }
767            if matches!(scalar.kind, crate::ScalarKind::Uint | crate::ScalarKind::Sint | crate::ScalarKind::Float)
768        ) {
769            log::error!("Subgroup gather operand type {argument_inner:?}");
770            return Err(SubgroupError::InvalidOperand(argument)
771                .with_span_handle(argument, context.expressions)
772                .into_other());
773        }
774
775        self.emit_expression(result, context)?;
776        match context.expressions[result] {
777            crate::Expression::SubgroupOperationResult { ty }
778                if { &context.types[ty].inner == argument_inner } => {}
779            _ => {
780                return Err(SubgroupError::ResultTypeMismatch(result)
781                    .with_span_handle(result, context.expressions)
782                    .into_other())
783            }
784        }
785        Ok(())
786    }
787
788    #[allow(clippy::large_stack_frames)] // TODO(https://github.com/gfx-rs/wgpu/issues/9456)
789    fn validate_block_impl(
790        &mut self,
791        statements: &crate::Block,
792        context: &BlockContext,
793    ) -> Result<BlockInfo, WithSpan<FunctionError>> {
794        use crate::{AddressSpace, Statement as S, TypeInner as Ti};
795        let mut stages = super::ShaderStages::all();
796        for (statement, &span) in statements.span_iter() {
797            match *statement {
798                S::Emit(ref range) => {
799                    for handle in range.clone() {
800                        use crate::Expression as Ex;
801                        match context.expressions[handle] {
802                            Ex::Literal(_)
803                            | Ex::Constant(_)
804                            | Ex::Override(_)
805                            | Ex::ZeroValue(_)
806                            | Ex::Compose { .. }
807                            | Ex::Access { .. }
808                            | Ex::AccessIndex { .. }
809                            | Ex::Splat { .. }
810                            | Ex::Swizzle { .. }
811                            | Ex::FunctionArgument(_)
812                            | Ex::GlobalVariable(_)
813                            | Ex::LocalVariable(_)
814                            | Ex::Load { .. }
815                            | Ex::ImageSample { .. }
816                            | Ex::ImageLoad { .. }
817                            | Ex::ImageQuery { .. }
818                            | Ex::Unary { .. }
819                            | Ex::Binary { .. }
820                            | Ex::Select { .. }
821                            | Ex::Derivative { .. }
822                            | Ex::Relational { .. }
823                            | Ex::Math { .. }
824                            | Ex::As { .. }
825                            | Ex::Bitcast { .. }
826                            | Ex::ArrayLength(_)
827                            | Ex::RayQueryGetIntersection { .. }
828                            | Ex::RayQueryVertexPositions { .. }
829                            | Ex::CooperativeLoad { .. }
830                            | Ex::CooperativeMultiplyAdd { .. } => {
831                                self.emit_expression(handle, context)?
832                            }
833                            Ex::CallResult(_)
834                            | Ex::AtomicResult { .. }
835                            | Ex::WorkGroupUniformLoadResult { .. }
836                            | Ex::RayQueryProceedResult
837                            | Ex::SubgroupBallotResult
838                            | Ex::SubgroupOperationResult { .. } => {
839                                return Err(FunctionError::EmitResult(handle)
840                                    .with_span_handle(handle, context.expressions));
841                            }
842                        }
843                    }
844                }
845                S::Block(ref block) => {
846                    let info = self.validate_block(block, context)?;
847                    stages &= info.stages;
848                }
849                S::If {
850                    condition,
851                    ref accept,
852                    ref reject,
853                } => {
854                    match *context.resolve_type_inner(condition, &self.valid_expression_set)? {
855                        Ti::Scalar(crate::Scalar {
856                            kind: crate::ScalarKind::Bool,
857                            width: _,
858                        }) => {}
859                        _ => {
860                            return Err(FunctionError::InvalidIfType(condition)
861                                .with_span_handle(condition, context.expressions))
862                        }
863                    }
864                    stages &= self.validate_block(accept, context)?.stages;
865                    stages &= self.validate_block(reject, context)?.stages;
866                }
867                S::Switch {
868                    selector,
869                    ref cases,
870                } => {
871                    let uint = match context
872                        .resolve_type_inner(selector, &self.valid_expression_set)?
873                        .scalar_kind()
874                    {
875                        Some(crate::ScalarKind::Uint) => true,
876                        Some(crate::ScalarKind::Sint) => false,
877                        _ => {
878                            return Err(FunctionError::InvalidSwitchType(selector)
879                                .with_span_handle(selector, context.expressions))
880                        }
881                    };
882                    self.switch_values.clear();
883                    for case in cases {
884                        match case.value {
885                            crate::SwitchValue::I32(_) if !uint => {}
886                            crate::SwitchValue::U32(_) if uint => {}
887                            crate::SwitchValue::Default => {}
888                            _ => {
889                                return Err(FunctionError::ConflictingCaseType.with_span_static(
890                                    case.body
891                                        .span_iter()
892                                        .next()
893                                        .map_or(Default::default(), |(_, s)| *s),
894                                    "conflicting switch arm here",
895                                ));
896                            }
897                        };
898                        if !self.switch_values.insert(case.value) {
899                            return Err(match case.value {
900                                crate::SwitchValue::Default => FunctionError::MultipleDefaultCases
901                                    .with_span_static(
902                                        case.body
903                                            .span_iter()
904                                            .next()
905                                            .map_or(Default::default(), |(_, s)| *s),
906                                        "duplicated switch arm here",
907                                    ),
908                                _ => FunctionError::ConflictingSwitchCase(case.value)
909                                    .with_span_static(
910                                        case.body
911                                            .span_iter()
912                                            .next()
913                                            .map_or(Default::default(), |(_, s)| *s),
914                                        "conflicting switch arm here",
915                                    ),
916                            });
917                        }
918                    }
919                    if !self.switch_values.contains(&crate::SwitchValue::Default) {
920                        return Err(FunctionError::MissingDefaultCase
921                            .with_span_static(span, "missing default case"));
922                    }
923                    if let Some(case) = cases.last() {
924                        if case.fall_through {
925                            return Err(FunctionError::LastCaseFallTrough.with_span_static(
926                                case.body
927                                    .span_iter()
928                                    .next()
929                                    .map_or(Default::default(), |(_, s)| *s),
930                                "bad switch arm here",
931                            ));
932                        }
933                    }
934                    let pass_through_abilities = context.abilities
935                        & (ControlFlowAbility::RETURN | ControlFlowAbility::CONTINUE);
936                    let sub_context =
937                        context.with_abilities(pass_through_abilities | ControlFlowAbility::BREAK);
938                    for case in cases {
939                        stages &= self.validate_block(&case.body, &sub_context)?.stages;
940                    }
941                }
942                S::Loop {
943                    ref body,
944                    ref continuing,
945                    break_if,
946                } => {
947                    // special handling for block scoping is needed here,
948                    // because the continuing{} block inherits the scope
949                    let base_expression_count = self.valid_expression_list.len();
950                    let pass_through_abilities = context.abilities & ControlFlowAbility::RETURN;
951                    stages &= self
952                        .validate_block_impl(
953                            body,
954                            &context.with_abilities(
955                                pass_through_abilities
956                                    | ControlFlowAbility::BREAK
957                                    | ControlFlowAbility::CONTINUE,
958                            ),
959                        )?
960                        .stages;
961                    stages &= self
962                        .validate_block_impl(
963                            continuing,
964                            &context.with_abilities(ControlFlowAbility::empty()),
965                        )?
966                        .stages;
967
968                    if let Some(condition) = break_if {
969                        match *context.resolve_type_inner(condition, &self.valid_expression_set)? {
970                            Ti::Scalar(crate::Scalar {
971                                kind: crate::ScalarKind::Bool,
972                                width: _,
973                            }) => {}
974                            _ => {
975                                return Err(FunctionError::InvalidIfType(condition)
976                                    .with_span_handle(condition, context.expressions))
977                            }
978                        }
979                    }
980
981                    for handle in self.valid_expression_list.drain(base_expression_count..) {
982                        self.valid_expression_set.remove(handle);
983                    }
984                }
985                S::Break => {
986                    if !context.abilities.contains(ControlFlowAbility::BREAK) {
987                        return Err(FunctionError::BreakOutsideOfLoopOrSwitch
988                            .with_span_static(span, "invalid break"));
989                    }
990                }
991                S::Continue => {
992                    if !context.abilities.contains(ControlFlowAbility::CONTINUE) {
993                        return Err(FunctionError::ContinueOutsideOfLoop
994                            .with_span_static(span, "invalid continue"));
995                    }
996                }
997                S::Return { value } => {
998                    if !context.abilities.contains(ControlFlowAbility::RETURN) {
999                        return Err(FunctionError::InvalidReturnSpot
1000                            .with_span_static(span, "invalid return"));
1001                    }
1002                    let value_ty = value
1003                        .map(|expr| context.resolve_type(expr, &self.valid_expression_set))
1004                        .transpose()?;
1005                    // We can't return pointers, but it seems best not to embed that
1006                    // assumption here, so use `TypeInner::equivalent` for comparison.
1007                    let okay = match (value_ty, context.return_type) {
1008                        (None, None) => true,
1009                        (Some(value_inner), Some(expected_ty)) => {
1010                            context.compare_types(value_inner, &TypeResolution::Handle(expected_ty))
1011                        }
1012                        (_, _) => false,
1013                    };
1014
1015                    if !okay {
1016                        log::error!(
1017                            "Returning {:?} where {:?} is expected",
1018                            value_ty,
1019                            context.return_type,
1020                        );
1021                        if let Some(handle) = value {
1022                            return Err(FunctionError::InvalidReturnType {
1023                                expression: value,
1024                                expected_ty: context.return_type,
1025                            }
1026                            .with_span_handle(handle, context.expressions));
1027                        } else {
1028                            return Err(FunctionError::InvalidReturnType {
1029                                expression: value,
1030                                expected_ty: context.return_type,
1031                            }
1032                            .with_span_static(span, "invalid return"));
1033                        }
1034                    }
1035                }
1036                S::Kill => {
1037                    stages &= super::ShaderStages::FRAGMENT;
1038                }
1039                S::ControlBarrier(barrier) | S::MemoryBarrier(barrier) => {
1040                    stages &= super::ShaderStages::COMPUTE_LIKE;
1041                    if barrier.contains(crate::Barrier::SUB_GROUP) {
1042                        if !self.capabilities.contains(
1043                            super::Capabilities::SUBGROUP | super::Capabilities::SUBGROUP_BARRIER,
1044                        ) {
1045                            return Err(FunctionError::MissingCapability(
1046                                super::Capabilities::SUBGROUP
1047                                    | super::Capabilities::SUBGROUP_BARRIER,
1048                            )
1049                            .with_span_static(span, "missing capability for this operation"));
1050                        }
1051                        if !self
1052                            .subgroup_operations
1053                            .contains(super::SubgroupOperationSet::BASIC)
1054                        {
1055                            return Err(FunctionError::InvalidSubgroup(
1056                                SubgroupError::UnsupportedOperation(
1057                                    super::SubgroupOperationSet::BASIC,
1058                                ),
1059                            )
1060                            .with_span_static(span, "support for this operation is not present"));
1061                        }
1062                    }
1063                }
1064                S::Store { pointer, value } => {
1065                    let mut current = pointer;
1066                    loop {
1067                        match context.expressions[current] {
1068                            crate::Expression::Access { base, .. }
1069                            | crate::Expression::AccessIndex { base, .. } => current = base,
1070                            crate::Expression::LocalVariable(_)
1071                            | crate::Expression::GlobalVariable(_)
1072                            | crate::Expression::FunctionArgument(_) => break,
1073                            _ => {
1074                                return Err(FunctionError::InvalidStorePointer(current)
1075                                    .with_span_handle(pointer, context.expressions))
1076                            }
1077                        }
1078                    }
1079
1080                    let value_tr = context.resolve_type(value, &self.valid_expression_set)?;
1081                    let value_ty = value_tr.inner_with(context.types);
1082                    match *value_ty {
1083                        Ti::Image { .. } | Ti::Sampler { .. } => {
1084                            return Err(FunctionError::InvalidStoreTexture {
1085                                actual: value,
1086                                actual_ty: value_ty.clone(),
1087                            }
1088                            .with_span_context((
1089                                context.expressions.get_span(value),
1090                                format!("this value is of type {value_ty:?}"),
1091                            ))
1092                            .with_span(span, "expects a texture argument"));
1093                        }
1094                        _ => {}
1095                    }
1096
1097                    let pointer_ty = context.resolve_pointer_type(pointer);
1098                    let pointer_base_tr = pointer_ty.pointer_base_type();
1099                    let pointer_base_ty = pointer_base_tr
1100                        .as_ref()
1101                        .map(|ty| ty.inner_with(context.types));
1102                    let good = if let Some(&Ti::Atomic(ref scalar)) = pointer_base_ty {
1103                        // The Naga IR allows storing a scalar to an atomic.
1104                        *value_ty == Ti::Scalar(*scalar)
1105                    } else if let Some(&Ti::RayQuery { .. }) = pointer_base_ty {
1106                        return Err(FunctionError::RayQueryStore(pointer)
1107                            .with_span_context((
1108                                context.expressions.get_span(pointer),
1109                                format!("this pointer has a base type of {pointer_base_ty:?} which cannot be stored to"),
1110                            ))
1111                            .with_span(span, "store to a type which is not allowed to be stored to"));
1112                    } else if let Some(tr) = pointer_base_tr {
1113                        context.compare_types(value_tr, &tr)
1114                    } else {
1115                        false
1116                    };
1117
1118                    if !good {
1119                        return Err(FunctionError::InvalidStoreTypes { pointer, value }
1120                            .with_span()
1121                            .with_handle(pointer, context.expressions)
1122                            .with_handle(value, context.expressions));
1123                    }
1124
1125                    if let Some(space) = pointer_ty.pointer_space() {
1126                        if !space.access().contains(crate::StorageAccess::STORE) {
1127                            return Err(FunctionError::InvalidStorePointer(pointer)
1128                                .with_span_static(
1129                                    context.expressions.get_span(pointer),
1130                                    "writing to this location is not permitted",
1131                                ));
1132                        }
1133                    }
1134                }
1135                S::ImageStore {
1136                    image,
1137                    coordinate,
1138                    array_index,
1139                    value,
1140                } => {
1141                    //Note: this code uses a lot of `FunctionError::InvalidImageStore`,
1142                    // and could probably be refactored.
1143                    let global_var;
1144                    let image_ty;
1145                    match *context.get_expression(image) {
1146                        crate::Expression::GlobalVariable(var_handle) => {
1147                            global_var = &context.global_vars[var_handle];
1148                            image_ty = global_var.ty;
1149                        }
1150                        // The `image` operand is indexing into a binding array,
1151                        // so punch through the `Access`* expression and look at
1152                        // the global behind it.
1153                        crate::Expression::Access { base, .. }
1154                        | crate::Expression::AccessIndex { base, .. } => {
1155                            let crate::Expression::GlobalVariable(var_handle) =
1156                                *context.get_expression(base)
1157                            else {
1158                                return Err(FunctionError::InvalidImageStore(
1159                                    ExpressionError::ExpectedGlobalVariable,
1160                                )
1161                                .with_span_handle(image, context.expressions));
1162                            };
1163                            global_var = &context.global_vars[var_handle];
1164
1165                            // The global variable must be a binding array.
1166                            let Ti::BindingArray { base, .. } = context.types[global_var.ty].inner
1167                            else {
1168                                return Err(FunctionError::InvalidImageStore(
1169                                    ExpressionError::ExpectedBindingArrayType(global_var.ty),
1170                                )
1171                                .with_span_handle(global_var.ty, context.types));
1172                            };
1173
1174                            image_ty = base;
1175                        }
1176                        _ => {
1177                            return Err(FunctionError::InvalidImageStore(
1178                                ExpressionError::ExpectedGlobalVariable,
1179                            )
1180                            .with_span_handle(image, context.expressions))
1181                        }
1182                    };
1183
1184                    // The `image` operand must be an `Image`.
1185                    let Ti::Image {
1186                        class,
1187                        arrayed,
1188                        dim,
1189                    } = context.types[image_ty].inner
1190                    else {
1191                        return Err(FunctionError::InvalidImageStore(
1192                            ExpressionError::ExpectedImageType(global_var.ty),
1193                        )
1194                        .with_span()
1195                        .with_handle(global_var.ty, context.types)
1196                        .with_handle(image, context.expressions));
1197                    };
1198
1199                    // It had better be a storage image, since we're writing to it.
1200                    let crate::ImageClass::Storage { format, .. } = class else {
1201                        return Err(FunctionError::InvalidImageStore(
1202                            ExpressionError::InvalidImageClass(class),
1203                        )
1204                        .with_span_handle(image, context.expressions));
1205                    };
1206
1207                    // The `coordinate` operand must be a vector of the appropriate size.
1208                    if context
1209                        .resolve_type_inner(coordinate, &self.valid_expression_set)?
1210                        .image_storage_coordinates()
1211                        .is_none_or(|coord_dim| coord_dim != dim)
1212                    {
1213                        return Err(FunctionError::InvalidImageStore(
1214                            ExpressionError::InvalidImageCoordinateType(dim, coordinate),
1215                        )
1216                        .with_span_handle(coordinate, context.expressions));
1217                    }
1218
1219                    // The `array_index` operand should be present if and only if
1220                    // the image itself is arrayed.
1221                    if arrayed != array_index.is_some() {
1222                        return Err(FunctionError::InvalidImageStore(
1223                            ExpressionError::InvalidImageArrayIndex,
1224                        )
1225                        .with_span_handle(coordinate, context.expressions));
1226                    }
1227
1228                    // If present, `array_index` must be a scalar integer type.
1229                    if let Some(expr) = array_index {
1230                        if !matches!(
1231                            *context.resolve_type_inner(expr, &self.valid_expression_set)?,
1232                            Ti::Scalar(crate::Scalar {
1233                                kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
1234                                width: _,
1235                            })
1236                        ) {
1237                            return Err(FunctionError::InvalidImageStore(
1238                                ExpressionError::InvalidImageArrayIndexType(expr),
1239                            )
1240                            .with_span_handle(expr, context.expressions));
1241                        }
1242                    }
1243
1244                    let value_ty = crate::TypeInner::Vector {
1245                        size: crate::VectorSize::Quad,
1246                        scalar: format.into(),
1247                    };
1248
1249                    // The value we're writing had better match the scalar type
1250                    // for `image`'s format.
1251                    let actual_value_ty =
1252                        context.resolve_type_inner(value, &self.valid_expression_set)?;
1253                    if actual_value_ty != &value_ty {
1254                        return Err(FunctionError::InvalidStoreValue {
1255                            actual: value,
1256                            actual_ty: actual_value_ty.clone(),
1257                            expected_ty: value_ty.clone(),
1258                        }
1259                        .with_span_context((
1260                            context.expressions.get_span(value),
1261                            format!("this value is of type {actual_value_ty:?}"),
1262                        ))
1263                        .with_span(
1264                            span,
1265                            format!("expects a value argument of type {value_ty:?}"),
1266                        ));
1267                    }
1268                }
1269                S::Call {
1270                    function,
1271                    ref arguments,
1272                    result,
1273                } => match self.validate_call(function, arguments, result, context) {
1274                    Ok(callee_stages) => stages &= callee_stages,
1275                    Err(error) => {
1276                        return Err(error.and_then(|error| {
1277                            FunctionError::InvalidCall { function, error }
1278                                .with_span_static(span, "invalid function call")
1279                        }))
1280                    }
1281                },
1282                S::Atomic {
1283                    pointer,
1284                    ref fun,
1285                    value,
1286                    result,
1287                } => {
1288                    self.validate_atomic(pointer, fun, value, result, span, context)?;
1289                }
1290                S::ImageAtomic {
1291                    image,
1292                    coordinate,
1293                    array_index,
1294                    fun,
1295                    value,
1296                } => {
1297                    let var = match *context.get_expression(image) {
1298                        crate::Expression::GlobalVariable(var_handle) => {
1299                            &context.global_vars[var_handle]
1300                        }
1301                        // We're looking at a binding index situation, so punch through the index and look at the global behind it.
1302                        crate::Expression::Access { base, .. }
1303                        | crate::Expression::AccessIndex { base, .. } => {
1304                            match *context.get_expression(base) {
1305                                crate::Expression::GlobalVariable(var_handle) => {
1306                                    &context.global_vars[var_handle]
1307                                }
1308                                _ => {
1309                                    return Err(FunctionError::InvalidImageAtomic(
1310                                        ExpressionError::ExpectedGlobalVariable,
1311                                    )
1312                                    .with_span_handle(image, context.expressions))
1313                                }
1314                            }
1315                        }
1316                        _ => {
1317                            return Err(FunctionError::InvalidImageAtomic(
1318                                ExpressionError::ExpectedGlobalVariable,
1319                            )
1320                            .with_span_handle(image, context.expressions))
1321                        }
1322                    };
1323
1324                    // Punch through a binding array to get the underlying type
1325                    let global_ty = match context.types[var.ty].inner {
1326                        Ti::BindingArray { base, .. } => &context.types[base].inner,
1327                        ref inner => inner,
1328                    };
1329
1330                    let value_ty = match *global_ty {
1331                        Ti::Image {
1332                            class,
1333                            arrayed,
1334                            dim,
1335                        } => {
1336                            match context
1337                                .resolve_type_inner(coordinate, &self.valid_expression_set)?
1338                                .image_storage_coordinates()
1339                            {
1340                                Some(coord_dim) if coord_dim == dim => {}
1341                                _ => {
1342                                    return Err(FunctionError::InvalidImageAtomic(
1343                                        ExpressionError::InvalidImageCoordinateType(
1344                                            dim, coordinate,
1345                                        ),
1346                                    )
1347                                    .with_span_handle(coordinate, context.expressions));
1348                                }
1349                            };
1350                            if arrayed != array_index.is_some() {
1351                                return Err(FunctionError::InvalidImageAtomic(
1352                                    ExpressionError::InvalidImageArrayIndex,
1353                                )
1354                                .with_span_handle(coordinate, context.expressions));
1355                            }
1356                            if let Some(expr) = array_index {
1357                                match *context
1358                                    .resolve_type_inner(expr, &self.valid_expression_set)?
1359                                {
1360                                    Ti::Scalar(crate::Scalar {
1361                                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
1362                                        width: _,
1363                                    }) => {}
1364                                    _ => {
1365                                        return Err(FunctionError::InvalidImageAtomic(
1366                                            ExpressionError::InvalidImageArrayIndexType(expr),
1367                                        )
1368                                        .with_span_handle(expr, context.expressions));
1369                                    }
1370                                }
1371                            }
1372                            match class {
1373                                crate::ImageClass::Storage { format, access } => {
1374                                    if !access.contains(crate::StorageAccess::ATOMIC) {
1375                                        return Err(FunctionError::InvalidImageAtomic(
1376                                            ExpressionError::InvalidImageStorageAccess(access),
1377                                        )
1378                                        .with_span_handle(image, context.expressions));
1379                                    }
1380                                    match format {
1381                                        crate::StorageFormat::R64Uint => {
1382                                            if !self.capabilities.intersects(
1383                                                super::Capabilities::TEXTURE_INT64_ATOMIC,
1384                                            ) {
1385                                                return Err(FunctionError::MissingCapability(
1386                                                    super::Capabilities::TEXTURE_INT64_ATOMIC,
1387                                                )
1388                                                .with_span_static(
1389                                                    span,
1390                                                    "missing capability for this operation",
1391                                                ));
1392                                            }
1393                                            match fun {
1394                                                crate::AtomicFunction::Min
1395                                                | crate::AtomicFunction::Max => {}
1396                                                _ => {
1397                                                    return Err(
1398                                                        FunctionError::InvalidImageAtomicFunction(
1399                                                            fun,
1400                                                        )
1401                                                        .with_span_handle(
1402                                                            image,
1403                                                            context.expressions,
1404                                                        ),
1405                                                    );
1406                                                }
1407                                            }
1408                                        }
1409                                        crate::StorageFormat::R32Sint
1410                                        | crate::StorageFormat::R32Uint => {
1411                                            if !self
1412                                                .capabilities
1413                                                .intersects(super::Capabilities::TEXTURE_ATOMIC)
1414                                            {
1415                                                return Err(FunctionError::MissingCapability(
1416                                                    super::Capabilities::TEXTURE_ATOMIC,
1417                                                )
1418                                                .with_span_static(
1419                                                    span,
1420                                                    "missing capability for this operation",
1421                                                ));
1422                                            }
1423                                            match fun {
1424                                                crate::AtomicFunction::Add
1425                                                | crate::AtomicFunction::And
1426                                                | crate::AtomicFunction::ExclusiveOr
1427                                                | crate::AtomicFunction::InclusiveOr
1428                                                | crate::AtomicFunction::Min
1429                                                | crate::AtomicFunction::Max => {}
1430                                                _ => {
1431                                                    return Err(
1432                                                        FunctionError::InvalidImageAtomicFunction(
1433                                                            fun,
1434                                                        )
1435                                                        .with_span_handle(
1436                                                            image,
1437                                                            context.expressions,
1438                                                        ),
1439                                                    );
1440                                                }
1441                                            }
1442                                        }
1443                                        _ => {
1444                                            return Err(FunctionError::InvalidImageAtomic(
1445                                                ExpressionError::InvalidImageFormat(format),
1446                                            )
1447                                            .with_span_handle(image, context.expressions));
1448                                        }
1449                                    }
1450                                    crate::TypeInner::Scalar(format.into())
1451                                }
1452                                _ => {
1453                                    return Err(FunctionError::InvalidImageAtomic(
1454                                        ExpressionError::InvalidImageClass(class),
1455                                    )
1456                                    .with_span_handle(image, context.expressions));
1457                                }
1458                            }
1459                        }
1460                        _ => {
1461                            return Err(FunctionError::InvalidImageAtomic(
1462                                ExpressionError::ExpectedImageType(var.ty),
1463                            )
1464                            .with_span()
1465                            .with_handle(var.ty, context.types)
1466                            .with_handle(image, context.expressions))
1467                        }
1468                    };
1469
1470                    if *context.resolve_type_inner(value, &self.valid_expression_set)? != value_ty {
1471                        return Err(FunctionError::InvalidImageAtomicValue(value)
1472                            .with_span_handle(value, context.expressions));
1473                    }
1474                }
1475                S::WorkGroupUniformLoad { pointer, result } => {
1476                    stages &= super::ShaderStages::COMPUTE_LIKE;
1477                    let pointer_inner =
1478                        context.resolve_type_inner(pointer, &self.valid_expression_set)?;
1479                    match *pointer_inner {
1480                        Ti::Pointer {
1481                            space: AddressSpace::WorkGroup,
1482                            ..
1483                        } => {}
1484                        Ti::ValuePointer {
1485                            space: AddressSpace::WorkGroup,
1486                            ..
1487                        } => {}
1488                        _ => {
1489                            return Err(FunctionError::WorkgroupUniformLoadInvalidPointer(pointer)
1490                                .with_span_static(span, "WorkGroupUniformLoad"))
1491                        }
1492                    }
1493                    self.emit_expression(result, context)?;
1494                    let ty = match &context.expressions[result] {
1495                        &crate::Expression::WorkGroupUniformLoadResult { ty } => ty,
1496                        _ => {
1497                            return Err(FunctionError::WorkgroupUniformLoadExpressionMismatch(
1498                                result,
1499                            )
1500                            .with_span_static(span, "WorkGroupUniformLoad"));
1501                        }
1502                    };
1503                    let expected_pointer_inner = Ti::Pointer {
1504                        base: ty,
1505                        space: AddressSpace::WorkGroup,
1506                    };
1507                    // workgroupUniformLoad on atomic<T> returns T, not atomic<T>.
1508                    // Verify the pointer's atomic scalar matches the result scalar.
1509                    let atomic_specialization_ok = match *pointer_inner {
1510                        Ti::Pointer {
1511                            base: pointer_base,
1512                            space: AddressSpace::WorkGroup,
1513                        } => match (&context.types[pointer_base].inner, &context.types[ty].inner) {
1514                            (&Ti::Atomic(pointer_scalar), &Ti::Scalar(result_scalar)) => {
1515                                pointer_scalar == result_scalar
1516                            }
1517                            _ => false,
1518                        },
1519                        _ => false,
1520                    };
1521                    if !expected_pointer_inner.non_struct_equivalent(pointer_inner, context.types)
1522                        && !atomic_specialization_ok
1523                    {
1524                        return Err(FunctionError::WorkgroupUniformLoadInvalidPointer(pointer)
1525                            .with_span_static(span, "WorkGroupUniformLoad"));
1526                    }
1527                }
1528                S::RayQuery { query, ref fun } => {
1529                    let query_var = match *context.get_expression(query) {
1530                        crate::Expression::LocalVariable(var) => &context.local_vars[var],
1531                        ref other => {
1532                            log::error!("Unexpected ray query expression {other:?}");
1533                            return Err(FunctionError::InvalidRayQueryExpression(query)
1534                                .with_span_static(span, "invalid query expression"));
1535                        }
1536                    };
1537                    let rq_vertex_return = match context.types[query_var.ty].inner {
1538                        Ti::RayQuery { vertex_return } => vertex_return,
1539                        ref other => {
1540                            log::error!("Unexpected ray query type {other:?}");
1541                            return Err(FunctionError::InvalidRayQueryType(query_var.ty)
1542                                .with_span_static(span, "invalid query type"));
1543                        }
1544                    };
1545                    match *fun {
1546                        crate::RayQueryFunction::Initialize {
1547                            acceleration_structure,
1548                            descriptor,
1549                        } => {
1550                            match *context.resolve_type_inner(
1551                                acceleration_structure,
1552                                &self.valid_expression_set,
1553                            )? {
1554                                Ti::AccelerationStructure { vertex_return } => {
1555                                    if (!vertex_return) && rq_vertex_return {
1556                                        return Err(FunctionError::MissingAccelerationStructureVertexReturn(acceleration_structure, query).with_span_static(span, "invalid acceleration structure"));
1557                                    }
1558                                }
1559                                _ => {
1560                                    return Err(FunctionError::InvalidAccelerationStructure(
1561                                        acceleration_structure,
1562                                    )
1563                                    .with_span_static(span, "invalid acceleration structure"))
1564                                }
1565                            }
1566                            let desc_ty_given = context
1567                                .resolve_type_inner(descriptor, &self.valid_expression_set)?;
1568                            let desc_ty_expected = context
1569                                .special_types
1570                                .ray_desc
1571                                .map(|handle| &context.types[handle].inner);
1572                            if Some(desc_ty_given) != desc_ty_expected {
1573                                return Err(FunctionError::InvalidRayDescriptor(descriptor)
1574                                    .with_span_static(span, "invalid ray descriptor"));
1575                            }
1576                        }
1577                        crate::RayQueryFunction::Proceed { result } => {
1578                            self.emit_expression(result, context)?;
1579                        }
1580                        crate::RayQueryFunction::GenerateIntersection { hit_t } => {
1581                            match *context.resolve_type_inner(hit_t, &self.valid_expression_set)? {
1582                                Ti::Scalar(crate::Scalar {
1583                                    kind: crate::ScalarKind::Float,
1584                                    width: _,
1585                                }) => {}
1586                                _ => {
1587                                    return Err(FunctionError::InvalidHitDistanceType(hit_t)
1588                                        .with_span_static(span, "invalid hit_t"))
1589                                }
1590                            }
1591                        }
1592                        crate::RayQueryFunction::ConfirmIntersection => {}
1593                        crate::RayQueryFunction::Terminate => {}
1594                        crate::RayQueryFunction::Begin => {}
1595                    }
1596                }
1597                S::SubgroupBallot { result, predicate } => {
1598                    stages &= self.subgroup_stages;
1599                    if !self.capabilities.contains(super::Capabilities::SUBGROUP) {
1600                        return Err(FunctionError::MissingCapability(
1601                            super::Capabilities::SUBGROUP,
1602                        )
1603                        .with_span_static(span, "missing capability for this operation"));
1604                    }
1605                    if !self
1606                        .subgroup_operations
1607                        .contains(super::SubgroupOperationSet::BALLOT)
1608                    {
1609                        return Err(FunctionError::InvalidSubgroup(
1610                            SubgroupError::UnsupportedOperation(
1611                                super::SubgroupOperationSet::BALLOT,
1612                            ),
1613                        )
1614                        .with_span_static(span, "support for this operation is not present"));
1615                    }
1616                    if let Some(predicate) = predicate {
1617                        let predicate_inner =
1618                            context.resolve_type_inner(predicate, &self.valid_expression_set)?;
1619                        if !matches!(
1620                            *predicate_inner,
1621                            crate::TypeInner::Scalar(crate::Scalar::BOOL,)
1622                        ) {
1623                            log::error!(
1624                                "Subgroup ballot predicate type {predicate_inner:?} expected bool"
1625                            );
1626                            return Err(SubgroupError::InvalidOperand(predicate)
1627                                .with_span_handle(predicate, context.expressions)
1628                                .into_other());
1629                        }
1630                    }
1631                    self.emit_expression(result, context)?;
1632                }
1633                S::SubgroupCollectiveOperation {
1634                    ref op,
1635                    ref collective_op,
1636                    argument,
1637                    result,
1638                } => {
1639                    stages &= self.subgroup_stages;
1640                    if !self.capabilities.contains(super::Capabilities::SUBGROUP) {
1641                        return Err(FunctionError::MissingCapability(
1642                            super::Capabilities::SUBGROUP,
1643                        )
1644                        .with_span_static(span, "missing capability for this operation"));
1645                    }
1646                    let operation = op.required_operations();
1647                    if !self.subgroup_operations.contains(operation) {
1648                        return Err(FunctionError::InvalidSubgroup(
1649                            SubgroupError::UnsupportedOperation(operation),
1650                        )
1651                        .with_span_static(span, "support for this operation is not present"));
1652                    }
1653                    self.validate_subgroup_operation(op, collective_op, argument, result, context)?;
1654                }
1655                S::SubgroupGather {
1656                    ref mode,
1657                    argument,
1658                    result,
1659                } => {
1660                    stages &= self.subgroup_stages;
1661                    if !self.capabilities.contains(super::Capabilities::SUBGROUP) {
1662                        return Err(FunctionError::MissingCapability(
1663                            super::Capabilities::SUBGROUP,
1664                        )
1665                        .with_span_static(span, "missing capability for this operation"));
1666                    }
1667                    let operation = mode.required_operations();
1668                    if !self.subgroup_operations.contains(operation) {
1669                        return Err(FunctionError::InvalidSubgroup(
1670                            SubgroupError::UnsupportedOperation(operation),
1671                        )
1672                        .with_span_static(span, "support for this operation is not present"));
1673                    }
1674                    self.validate_subgroup_gather(mode, argument, result, context)?;
1675                }
1676                S::CooperativeStore { target, ref data } => {
1677                    stages &= super::ShaderStages::COMPUTE;
1678
1679                    let target_scalar =
1680                        match *context.resolve_type_inner(target, &self.valid_expression_set)? {
1681                            Ti::CooperativeMatrix { scalar, .. } => scalar,
1682                            ref other => {
1683                                log::error!("Target operand type: {other:?}");
1684                                return Err(FunctionError::InvalidCooperativeStoreTarget(target)
1685                                    .with_span_handle(target, context.expressions));
1686                            }
1687                        };
1688
1689                    let ptr_ty = context.resolve_pointer_type(data.pointer);
1690                    let ptr_scalar = ptr_ty
1691                        .pointer_base_type()
1692                        .and_then(|tr| tr.inner_with(context.types).scalar());
1693                    if ptr_scalar != Some(target_scalar) {
1694                        return Err(FunctionError::InvalidCooperativeDataPointer(data.pointer)
1695                            .with_span_handle(data.pointer, context.expressions));
1696                    }
1697
1698                    let ptr_space = ptr_ty.pointer_space().unwrap_or(AddressSpace::Handle);
1699                    if !ptr_space.access().contains(crate::StorageAccess::STORE) {
1700                        return Err(FunctionError::InvalidStorePointer(data.pointer)
1701                            .with_span_static(
1702                                context.expressions.get_span(data.pointer),
1703                                "writing to this location is not permitted",
1704                            ));
1705                    }
1706                }
1707                S::RayPipelineFunction(ref fun) => match *fun {
1708                    crate::RayPipelineFunction::TraceRay {
1709                        acceleration_structure,
1710                        descriptor,
1711                        payload,
1712                    } => {
1713                        match *context.resolve_type_inner(
1714                            acceleration_structure,
1715                            &self.valid_expression_set,
1716                        )? {
1717                            crate::TypeInner::AccelerationStructure { vertex_return } => {
1718                                if !vertex_return {
1719                                    self.trace_rays_vertex_return =
1720                                        super::TraceRayVertexReturnState::NoVertexReturn(span);
1721                                } else if let super::TraceRayVertexReturnState::NoTraceRays =
1722                                    self.trace_rays_vertex_return
1723                                {
1724                                    self.trace_rays_vertex_return =
1725                                        super::TraceRayVertexReturnState::VertexReturn;
1726                                }
1727                            }
1728                            _ => {
1729                                return Err(FunctionError::InvalidAccelerationStructure(
1730                                    acceleration_structure,
1731                                )
1732                                .with_span_handle(acceleration_structure, context.expressions))
1733                            }
1734                        }
1735
1736                        let current_payload_ty = match *context
1737                            .resolve_type_inner(payload, &self.valid_expression_set)?
1738                        {
1739                            crate::TypeInner::Pointer { base, space } => {
1740                                match space {
1741                                    AddressSpace::RayPayload | AddressSpace::IncomingRayPayload => {
1742                                    }
1743                                    space => {
1744                                        return Err(FunctionError::InvalidPayloadAddressSpace(
1745                                            space,
1746                                        )
1747                                        .with_span_handle(payload, context.expressions))
1748                                    }
1749                                }
1750                                base
1751                            }
1752                            _ => {
1753                                return Err(FunctionError::InvalidPayloadType
1754                                    .with_span_handle(payload, context.expressions))
1755                            }
1756                        };
1757
1758                        // spir-v requires a direct reference to a global variable.
1759                        let crate::Expression::GlobalVariable(_) = context.expressions[payload]
1760                        else {
1761                            return Err(FunctionError::PayloadPointerNotGlobal
1762                                .with_span_handle(payload, context.expressions));
1763                        };
1764
1765                        let ty = *self
1766                            .trace_rays_payload_type
1767                            .get_or_insert(current_payload_ty);
1768
1769                        if ty != current_payload_ty {
1770                            return Err(FunctionError::MismatchedPayloadType(
1771                                current_payload_ty,
1772                                ty,
1773                            )
1774                            .with_span_handle(ty, context.types));
1775                        }
1776
1777                        let desc_ty_given =
1778                            context.resolve_type_inner(descriptor, &self.valid_expression_set)?;
1779                        let desc_ty_expected = context
1780                            .special_types
1781                            .ray_desc
1782                            .map(|handle| &context.types[handle].inner);
1783                        if Some(desc_ty_given) != desc_ty_expected {
1784                            return Err(FunctionError::InvalidRayDescriptor(descriptor)
1785                                .with_span_static(span, "invalid ray descriptor"));
1786                        }
1787                    }
1788                },
1789
1790                // TODO: the format string itself is not validated, so a mismatch between the
1791                // specifiers and the arguments is only caught by the backend's
1792                // shader logging implementation.
1793                S::DebugPrintf {
1794                    format: _,
1795                    ref arguments,
1796                } => {
1797                    if !self
1798                        .capabilities
1799                        .contains(super::Capabilities::DEBUG_PRINTF)
1800                    {
1801                        return Err(FunctionError::MissingCapability(
1802                            super::Capabilities::DEBUG_PRINTF,
1803                        )
1804                        .with_span_static(
1805                            span,
1806                            "`debugPrintf` requires the DEBUG_PRINTF capability",
1807                        ));
1808                    }
1809
1810                    for &argument in arguments {
1811                        let ty =
1812                            context.resolve_type_inner(argument, &self.valid_expression_set)?;
1813                        match *ty {
1814                            // Only scalar arguments are supported for now. Vector and
1815                            // matrix arguments could be supported in the future by
1816                            // splatting them into their components in the backends.
1817                            Ti::Scalar(_) => {}
1818                            _ => {
1819                                return Err(FunctionError::InvalidDebugPrintfArgument(argument)
1820                                    .with_span_handle(argument, context.expressions));
1821                            }
1822                        }
1823                    }
1824                }
1825            }
1826        }
1827        Ok(BlockInfo { stages })
1828    }
1829
1830    fn validate_block(
1831        &mut self,
1832        statements: &crate::Block,
1833        context: &BlockContext,
1834    ) -> Result<BlockInfo, WithSpan<FunctionError>> {
1835        let base_expression_count = self.valid_expression_list.len();
1836        let info = self.validate_block_impl(statements, context)?;
1837        for handle in self.valid_expression_list.drain(base_expression_count..) {
1838            self.valid_expression_set.remove(handle);
1839        }
1840        Ok(info)
1841    }
1842
1843    fn validate_local_var(
1844        &self,
1845        var: &crate::LocalVariable,
1846        gctx: crate::proc::GlobalCtx,
1847        fun_info: &FunctionInfo,
1848        local_expr_kind: &crate::proc::ExpressionKindTracker,
1849    ) -> Result<(), LocalVariableError> {
1850        log::debug!("var {var:?}");
1851        let type_info = self
1852            .types
1853            .get(var.ty.index())
1854            .ok_or(LocalVariableError::InvalidType(var.ty))?;
1855        if !type_info.flags.contains(super::TypeFlags::CONSTRUCTIBLE) {
1856            return Err(LocalVariableError::InvalidType(var.ty));
1857        }
1858
1859        if let Some(init) = var.init {
1860            if !gctx.compare_types(&TypeResolution::Handle(var.ty), &fun_info[init].ty) {
1861                return Err(LocalVariableError::InitializerType);
1862            }
1863
1864            if !local_expr_kind.is_const_or_override(init) {
1865                return Err(LocalVariableError::NonConstOrOverrideInitializer);
1866            }
1867
1868            if matches!(gctx.types[var.ty].inner, crate::TypeInner::RayQuery { .. }) {
1869                return Err(LocalVariableError::RayQueryWithInitializeExpression);
1870            }
1871        }
1872
1873        Ok(())
1874    }
1875
1876    pub(super) fn validate_function(
1877        &mut self,
1878        fun: &crate::Function,
1879        module: &crate::Module,
1880        mod_info: &ModuleInfo,
1881        entry_point: bool,
1882    ) -> Result<FunctionInfo, WithSpan<FunctionError>> {
1883        let mut info = mod_info.process_function(fun, module, self.flags, self.capabilities)?;
1884
1885        let local_expr_kind = crate::proc::ExpressionKindTracker::from_arena(&fun.expressions);
1886
1887        for (var_handle, var) in fun.local_variables.iter() {
1888            self.validate_local_var(var, module.to_ctx(), &info, &local_expr_kind)
1889                .map_err(|source| {
1890                    FunctionError::LocalVariable {
1891                        handle: var_handle,
1892                        name: var.name.clone().unwrap_or_default(),
1893                        source,
1894                    }
1895                    .with_span_handle(var.ty, &module.types)
1896                    .with_handle(var_handle, &fun.local_variables)
1897                })?;
1898        }
1899
1900        for (index, argument) in fun.arguments.iter().enumerate() {
1901            match module.types[argument.ty].inner.pointer_space() {
1902                Some(crate::AddressSpace::Private | crate::AddressSpace::Function) | None => {}
1903                Some(other) => {
1904                    return Err(FunctionError::InvalidArgumentPointerSpace {
1905                        index,
1906                        name: argument.name.clone().unwrap_or_default(),
1907                        space: other,
1908                    }
1909                    .with_span_handle(argument.ty, &module.types))
1910                }
1911            }
1912            // Check for the least informative error last.
1913            if !self.types[argument.ty.index()]
1914                .flags
1915                .contains(super::TypeFlags::ARGUMENT)
1916            {
1917                return Err(FunctionError::InvalidArgumentType {
1918                    index,
1919                    name: argument.name.clone().unwrap_or_default(),
1920                }
1921                .with_span_handle(argument.ty, &module.types));
1922            }
1923
1924            if !entry_point && argument.binding.is_some() {
1925                return Err(FunctionError::PipelineInputRegularFunction {
1926                    name: argument.name.clone().unwrap_or_default(),
1927                }
1928                .with_span_handle(argument.ty, &module.types));
1929            }
1930        }
1931
1932        if let Some(ref result) = fun.result {
1933            if !self.types[result.ty.index()]
1934                .flags
1935                .contains(super::TypeFlags::CONSTRUCTIBLE)
1936            {
1937                return Err(FunctionError::NonConstructibleReturnType
1938                    .with_span_handle(result.ty, &module.types));
1939            }
1940
1941            if !entry_point && result.binding.is_some() {
1942                return Err(FunctionError::PipelineOutputRegularFunction
1943                    .with_span_handle(result.ty, &module.types));
1944            }
1945        }
1946
1947        self.valid_expression_set.clear_for_arena(&fun.expressions);
1948        self.valid_expression_list.clear();
1949        self.needs_visit.clear_for_arena(&fun.expressions);
1950        for (handle, expr) in fun.expressions.iter() {
1951            if expr.needs_pre_emit() {
1952                self.valid_expression_set.insert(handle);
1953            }
1954            if self.flags.contains(super::ValidationFlags::EXPRESSIONS) {
1955                // Mark expressions that need to be visited by a particular kind of
1956                // statement.
1957                if let crate::Expression::CallResult(_) | crate::Expression::AtomicResult { .. } =
1958                    *expr
1959                {
1960                    self.needs_visit.insert(handle);
1961                }
1962
1963                match self.validate_expression(
1964                    handle,
1965                    expr,
1966                    fun,
1967                    module,
1968                    &info,
1969                    mod_info,
1970                    &local_expr_kind,
1971                ) {
1972                    Ok(stages) => info.available_stages &= stages,
1973                    Err(source) => {
1974                        return Err(FunctionError::Expression { handle, source }
1975                            .with_span_handle(handle, &fun.expressions))
1976                    }
1977                }
1978            }
1979        }
1980
1981        if self.flags.contains(super::ValidationFlags::BLOCKS) {
1982            let stages = self
1983                .validate_block(
1984                    &fun.body,
1985                    &BlockContext::new(fun, module, &info, &mod_info.functions, &local_expr_kind),
1986                )?
1987                .stages;
1988            info.available_stages &= stages;
1989
1990            if self.flags.contains(super::ValidationFlags::EXPRESSIONS) {
1991                if let Some(handle) = self.needs_visit.iter().next() {
1992                    return Err(FunctionError::UnvisitedExpression(handle)
1993                        .with_span_handle(handle, &fun.expressions));
1994                }
1995            }
1996        }
1997        Ok(info)
1998    }
1999}