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