naga/valid/
function.rs

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