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