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