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