naga/valid/
function.rs

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