Skip to main content

naga/valid/
handles.rs

1//! Implementation of `Validator::validate_module_handles`.
2
3use alloc::boxed::Box;
4use core::{convert::TryInto, hash::Hash};
5
6use super::{TypeError, ValidationError};
7use crate::non_max_u32::NonMaxU32;
8use crate::{
9    arena::{BadHandle, BadRangeError},
10    diagnostic_filter::DiagnosticFilterNode,
11    EntryPoint, Handle,
12};
13use crate::{Arena, UniqueArena};
14
15use alloc::string::ToString;
16
17impl super::Validator {
18    /// Validates that all handles within `module` are:
19    ///
20    /// * Valid, in the sense that they contain indices within each arena structure inside the
21    ///   [`crate::Module`] type.
22    /// * No arena contents contain any items that have forward dependencies; that is, the value
23    ///   associated with a handle only may contain references to handles in the same arena that
24    ///   were constructed before it.
25    ///
26    /// By validating the above conditions, we free up subsequent logic to assume that handle
27    /// accesses are infallible.
28    ///
29    /// # Errors
30    ///
31    /// Errors returned by this method are intentionally sparse, for simplicity of implementation.
32    /// It is expected that only buggy frontends or fuzzers should ever emit IR that fails this
33    /// validation pass.
34    pub(super) fn validate_module_handles(
35        module: &crate::Module,
36    ) -> Result<(), Box<ValidationError>> {
37        let &crate::Module {
38            ref constants,
39            ref overrides,
40            ref entry_points,
41            ref functions,
42            ref global_variables,
43            ref types,
44            ref special_types,
45            ref global_expressions,
46            ref diagnostic_filters,
47            ref diagnostic_filter_leaf,
48            ref doc_comments,
49        } = module;
50
51        // Because types can refer to global expressions and vice versa, to
52        // ensure the overall structure is free of cycles, we must traverse them
53        // both in tandem.
54        //
55        // Try to visit all types and global expressions in an order such that
56        // each item refers only to previously visited items. If we succeed,
57        // that shows that there cannot be any cycles, since walking any edge
58        // advances you towards the beginning of the visiting order.
59        //
60        // Validate all the handles in types and expressions as we traverse the
61        // arenas.
62        let mut global_exprs_iter = global_expressions.iter().peekable();
63        for (th, t) in types.iter() {
64            // Imagine the `for` loop and `global_exprs_iter` as two fingers
65            // walking the type and global expression arenas. They don't visit
66            // elements at the same rate: sometimes one processes a bunch of
67            // elements while the other one stays still. But at each point, they
68            // check that the two ranges of elements they've visited only refer
69            // to other elements in those ranges.
70            //
71            // For brevity, we'll say 'handles behind `global_exprs_iter`' to
72            // mean handles that have already been produced by
73            // `global_exprs_iter`. Once `global_exprs_iter` returns `None`, all
74            // global expression handles are 'behind' it.
75            //
76            // At this point:
77            //
78            // - All types visited by prior iterations (that is, before
79            //   `th`/`t`) refer only to expressions behind `global_exprs_iter`.
80            //
81            //   On the first iteration, this is obviously true: there are no
82            //   prior iterations, and `global_exprs_iter` hasn't produced
83            //   anything yet. At the bottom of the loop, we'll claim that it's
84            //   true for `th`/`t` as well, so the condition remains true when
85            //   we advance to the next type.
86            //
87            // - All expressions behind `global_exprs_iter` refer only to
88            //   previously visited types.
89            //
90            //   Again, trivially true at the start, and we'll show it's true
91            //   about each expression that `global_exprs_iter` produces.
92            //
93            // Once we also check that arena elements only refer to prior
94            // elements in that arena, we can see that `th`/`t` does not
95            // participate in a cycle: it only refers to previously visited
96            // types and expressions behind `global_exprs_iter`, and none of
97            // those refer to `th`/`t`, because they passed the same checks
98            // before we reached `th`/`t`.
99            if let Some(max_expr) = Self::validate_type_handles((th, t), overrides)? {
100                max_expr.check_valid_for(global_expressions)?;
101                // Since `t` refers to `max_expr`, if we want our invariants to
102                // remain true, we must advance `global_exprs_iter` beyond
103                // `max_expr`.
104                while let Some((eh, e)) = global_exprs_iter.next_if(|&(eh, _)| eh <= max_expr) {
105                    if let Some(max_type) =
106                        Self::validate_const_expression_handles((eh, e), constants, overrides)?
107                    {
108                        // Show that `eh` refers only to previously visited types.
109                        th.check_dep(max_type)?;
110                    }
111                    // We've advanced `global_exprs_iter` past `eh` already. But
112                    // since we now know that `eh` refers only to previously
113                    // visited types, it is again true that all expressions
114                    // behind `global_exprs_iter` refer only to previously
115                    // visited types. So we can continue to the next expression.
116                }
117            }
118
119            // Here we know that if `th` refers to any expressions at all,
120            // `max_expr` is the latest one. And we know that `max_expr` is
121            // behind `global_exprs_iter`. So `th` refers only to expressions
122            // behind `global_exprs_iter`, and the invariants will still be
123            // true on the next iteration.
124        }
125
126        // Since we also enforced the usual intra-arena rules that expressions
127        // refer only to prior expressions, expressions can only form cycles if
128        // they include types. But we've shown that all types are acyclic, so
129        // all expressions must be acyclic as well.
130        //
131        // Validate the remaining expressions normally.
132        for handle_and_expr in global_exprs_iter {
133            Self::validate_const_expression_handles(handle_and_expr, constants, overrides)?;
134        }
135
136        let validate_type = |handle| Self::validate_type_handle(handle, types);
137        let validate_const_expr =
138            |handle| Self::validate_expression_handle(handle, global_expressions);
139
140        for (_handle, constant) in constants.iter() {
141            let &crate::Constant { name: _, ty, init } = constant;
142            validate_type(ty)?;
143            validate_const_expr(init)?;
144        }
145
146        for (_handle, r#override) in overrides.iter() {
147            let &crate::Override {
148                name: _,
149                id: _,
150                ty,
151                init,
152            } = r#override;
153            validate_type(ty)?;
154            if let Some(init_expr) = init {
155                validate_const_expr(init_expr)?;
156            }
157        }
158
159        for (_handle, global_variable) in global_variables.iter() {
160            let &crate::GlobalVariable {
161                name: _,
162                space: _,
163                binding: _,
164                ty,
165                init,
166                memory_decorations: _,
167            } = global_variable;
168            validate_type(ty)?;
169            if let Some(init_expr) = init {
170                validate_const_expr(init_expr)?;
171            }
172        }
173
174        let validate_function = |function_handle, function: &_| -> Result<_, InvalidHandleError> {
175            let &crate::Function {
176                name: _,
177                ref arguments,
178                ref result,
179                ref local_variables,
180                ref expressions,
181                ref named_expressions,
182                ref body,
183                ref diagnostic_filter_leaf,
184            } = function;
185
186            for arg in arguments.iter() {
187                let &crate::FunctionArgument {
188                    name: _,
189                    ty,
190                    binding: _,
191                } = arg;
192                validate_type(ty)?;
193            }
194
195            if let &Some(crate::FunctionResult { ty, binding: _ }) = result {
196                validate_type(ty)?;
197            }
198
199            for (_handle, local_variable) in local_variables.iter() {
200                let &crate::LocalVariable { name: _, ty, init } = local_variable;
201                validate_type(ty)?;
202                if let Some(init) = init {
203                    Self::validate_expression_handle(init, expressions)?;
204                }
205            }
206
207            for handle in named_expressions.keys().copied() {
208                Self::validate_expression_handle(handle, expressions)?;
209            }
210
211            for handle_and_expr in expressions.iter() {
212                Self::validate_expression_handles(
213                    handle_and_expr,
214                    constants,
215                    overrides,
216                    types,
217                    local_variables,
218                    global_variables,
219                    functions,
220                    function_handle,
221                )?;
222            }
223
224            Self::validate_block_handles(body, expressions, functions)?;
225
226            if let Some(handle) = *diagnostic_filter_leaf {
227                handle.check_valid_for(diagnostic_filters)?;
228            }
229
230            Ok(())
231        };
232
233        for entry_point in entry_points.iter() {
234            validate_function(None, &entry_point.function)?;
235            if let Some(sizes) = entry_point.workgroup_size_overrides {
236                for size in sizes.iter().filter_map(|x| *x) {
237                    validate_const_expr(size)?;
238                }
239            }
240            if let Some(task_payload) = entry_point.task_payload {
241                Self::validate_global_variable_handle(task_payload, global_variables)?;
242            }
243            if let Some(ref mesh_info) = entry_point.mesh_info {
244                Self::validate_global_variable_handle(mesh_info.output_variable, global_variables)?;
245                validate_type(mesh_info.vertex_output_type)?;
246                validate_type(mesh_info.primitive_output_type)?;
247                for ov in mesh_info
248                    .max_vertices_override
249                    .iter()
250                    .chain(mesh_info.max_primitives_override.iter())
251                {
252                    validate_const_expr(*ov)?;
253                }
254            }
255        }
256
257        for (function_handle, function) in functions.iter() {
258            validate_function(Some(function_handle), function)?;
259        }
260
261        if let Some(ty) = special_types.ray_desc {
262            validate_type(ty)?;
263        }
264        if let Some(ty) = special_types.ray_intersection {
265            validate_type(ty)?;
266        }
267        if let Some(ty) = special_types.ray_vertex_return {
268            validate_type(ty)?;
269        }
270
271        for (handle, _node) in diagnostic_filters.iter() {
272            let DiagnosticFilterNode { inner: _, parent } = diagnostic_filters[handle];
273            handle.check_dep_opt(parent)?;
274        }
275        if let Some(handle) = *diagnostic_filter_leaf {
276            handle.check_valid_for(diagnostic_filters)?;
277        }
278
279        if let Some(doc_comments) = doc_comments.as_ref() {
280            let crate::DocComments {
281                module: _,
282                types: ref doc_comments_for_types,
283                struct_members: ref doc_comments_for_struct_members,
284                entry_points: ref doc_comments_for_entry_points,
285                functions: ref doc_comments_for_functions,
286                constants: ref doc_comments_for_constants,
287                global_variables: ref doc_comments_for_global_variables,
288            } = **doc_comments;
289
290            for (&ty, _) in doc_comments_for_types.iter() {
291                validate_type(ty)?;
292            }
293
294            for (&(ty, struct_member_index), _) in doc_comments_for_struct_members.iter() {
295                validate_type(ty)?;
296                let struct_type = types.get_handle(ty).unwrap();
297                match struct_type.inner {
298                    crate::TypeInner::Struct {
299                        ref members,
300                        span: ref _span,
301                    } => {
302                        (0..members.len())
303                            .contains(&struct_member_index)
304                            .then_some(())
305                            // TODO: what errors should this be?
306                            .ok_or_else(|| ValidationError::Type {
307                                handle: ty,
308                                name: struct_type.name.as_ref().map_or_else(
309                                    || "members length incorrect".to_string(),
310                                    |name| name.to_string(),
311                                ),
312                                source: TypeError::InvalidData(ty),
313                            })?;
314                    }
315                    _ => {
316                        // TODO: internal error ? We should never get here.
317                        // If entering there, it's probably that we forgot to adjust a handle in the compact phase.
318                        return Err(Box::new(ValidationError::Type {
319                            handle: ty,
320                            name: struct_type
321                                .name
322                                .as_ref()
323                                .map_or_else(|| "Unknown".to_string(), |name| name.to_string()),
324                            source: TypeError::InvalidData(ty),
325                        }));
326                    }
327                }
328                for (&function, _) in doc_comments_for_functions.iter() {
329                    Self::validate_function_handle(function, functions)?;
330                }
331                for (&entry_point_index, _) in doc_comments_for_entry_points.iter() {
332                    Self::validate_entry_point_index(entry_point_index, entry_points)?;
333                }
334                for (&constant, _) in doc_comments_for_constants.iter() {
335                    Self::validate_constant_handle(constant, constants)?;
336                }
337                for (&global_variable, _) in doc_comments_for_global_variables.iter() {
338                    Self::validate_global_variable_handle(global_variable, global_variables)?;
339                }
340            }
341        }
342
343        Ok(())
344    }
345
346    fn validate_type_handle(
347        handle: Handle<crate::Type>,
348        types: &UniqueArena<crate::Type>,
349    ) -> Result<(), InvalidHandleError> {
350        handle.check_valid_for_uniq(types).map(|_| ())
351    }
352
353    fn validate_constant_handle(
354        handle: Handle<crate::Constant>,
355        constants: &Arena<crate::Constant>,
356    ) -> Result<(), InvalidHandleError> {
357        handle.check_valid_for(constants).map(|_| ())
358    }
359
360    fn validate_global_variable_handle(
361        handle: Handle<crate::GlobalVariable>,
362        global_variables: &Arena<crate::GlobalVariable>,
363    ) -> Result<(), InvalidHandleError> {
364        handle.check_valid_for(global_variables).map(|_| ())
365    }
366
367    fn validate_override_handle(
368        handle: Handle<crate::Override>,
369        overrides: &Arena<crate::Override>,
370    ) -> Result<(), InvalidHandleError> {
371        handle.check_valid_for(overrides).map(|_| ())
372    }
373
374    fn validate_expression_handle(
375        handle: Handle<crate::Expression>,
376        expressions: &Arena<crate::Expression>,
377    ) -> Result<(), InvalidHandleError> {
378        handle.check_valid_for(expressions).map(|_| ())
379    }
380
381    fn validate_function_handle(
382        handle: Handle<crate::Function>,
383        functions: &Arena<crate::Function>,
384    ) -> Result<(), InvalidHandleError> {
385        handle.check_valid_for(functions).map(|_| ())
386    }
387
388    /// Validate all handles that occur in `ty`, whose handle is `handle`.
389    ///
390    /// If `ty` refers to any expressions, return the highest-indexed expression
391    /// handle that it uses. This is used for detecting cycles between the
392    /// expression and type arenas.
393    fn validate_type_handles(
394        (handle, ty): (Handle<crate::Type>, &crate::Type),
395        overrides: &Arena<crate::Override>,
396    ) -> Result<Option<Handle<crate::Expression>>, InvalidHandleError> {
397        let max_expr = match ty.inner {
398            crate::TypeInner::Scalar { .. }
399            | crate::TypeInner::Vector { .. }
400            | crate::TypeInner::Matrix { .. }
401            | crate::TypeInner::CooperativeMatrix { .. }
402            | crate::TypeInner::ValuePointer { .. }
403            | crate::TypeInner::Atomic { .. }
404            | crate::TypeInner::Image { .. }
405            | crate::TypeInner::Sampler { .. }
406            | crate::TypeInner::AccelerationStructure { .. }
407            | crate::TypeInner::RayQuery { .. } => None,
408            crate::TypeInner::Pointer { base, space: _ } => {
409                handle.check_dep(base)?;
410                None
411            }
412            crate::TypeInner::Array { base, size, .. }
413            | crate::TypeInner::BindingArray { base, size, .. } => {
414                handle.check_dep(base)?;
415                match size {
416                    crate::ArraySize::Pending(h) => {
417                        Self::validate_override_handle(h, overrides)?;
418                        let r#override = &overrides[h];
419                        handle.check_dep(r#override.ty)?;
420                        r#override.init
421                    }
422                    crate::ArraySize::Constant(_) | crate::ArraySize::Dynamic => None,
423                }
424            }
425            crate::TypeInner::Struct {
426                ref members,
427                span: _,
428            } => {
429                handle.check_dep_iter(members.iter().map(|m| m.ty))?;
430                None
431            }
432        };
433
434        Ok(max_expr)
435    }
436
437    fn validate_entry_point_index(
438        entry_point_index: usize,
439        entry_points: &[EntryPoint],
440    ) -> Result<(), InvalidHandleError> {
441        (0..entry_points.len())
442            .contains(&entry_point_index)
443            .then_some(())
444            .ok_or_else(|| {
445                BadHandle {
446                    kind: "EntryPoint",
447                    index: entry_point_index,
448                }
449                .into()
450            })
451    }
452
453    /// Validate all handles that occur in `expression`, whose handle is `handle`.
454    ///
455    /// If `expression` refers to any `Type`s, return the highest-indexed type
456    /// handle that it uses. This is used for detecting cycles between the
457    /// expression and type arenas.
458    fn validate_const_expression_handles(
459        (handle, expression): (Handle<crate::Expression>, &crate::Expression),
460        constants: &Arena<crate::Constant>,
461        overrides: &Arena<crate::Override>,
462    ) -> Result<Option<Handle<crate::Type>>, InvalidHandleError> {
463        let validate_constant = |handle| Self::validate_constant_handle(handle, constants);
464        let validate_override = |handle| Self::validate_override_handle(handle, overrides);
465
466        let max_type = match *expression {
467            crate::Expression::Literal(_) => None,
468            crate::Expression::Constant(constant) => {
469                validate_constant(constant)?;
470                handle.check_dep(constants[constant].init)?;
471                None
472            }
473            crate::Expression::Override(r#override) => {
474                validate_override(r#override)?;
475                if let Some(init) = overrides[r#override].init {
476                    handle.check_dep(init)?;
477                }
478                None
479            }
480            crate::Expression::ZeroValue(ty) => Some(ty),
481            crate::Expression::Compose { ty, ref components } => {
482                handle.check_dep_iter(components.iter().copied())?;
483                Some(ty)
484            }
485            _ => None,
486        };
487        Ok(max_type)
488    }
489
490    #[allow(clippy::too_many_arguments)]
491    fn validate_expression_handles(
492        (handle, expression): (Handle<crate::Expression>, &crate::Expression),
493        constants: &Arena<crate::Constant>,
494        overrides: &Arena<crate::Override>,
495        types: &UniqueArena<crate::Type>,
496        local_variables: &Arena<crate::LocalVariable>,
497        global_variables: &Arena<crate::GlobalVariable>,
498        functions: &Arena<crate::Function>,
499        // The handle of the current function or `None` if it's an entry point
500        current_function: Option<Handle<crate::Function>>,
501    ) -> Result<(), InvalidHandleError> {
502        let validate_constant = |handle| Self::validate_constant_handle(handle, constants);
503        let validate_override = |handle| Self::validate_override_handle(handle, overrides);
504        let validate_type = |handle| Self::validate_type_handle(handle, types);
505
506        match *expression {
507            crate::Expression::Access { base, index } => {
508                handle.check_dep(base)?.check_dep(index)?;
509            }
510            crate::Expression::AccessIndex { base, .. } => {
511                handle.check_dep(base)?;
512            }
513            crate::Expression::Splat { value, .. } => {
514                handle.check_dep(value)?;
515            }
516            crate::Expression::Swizzle { vector, .. } => {
517                handle.check_dep(vector)?;
518            }
519            crate::Expression::Literal(_) => {}
520            crate::Expression::Constant(constant) => {
521                validate_constant(constant)?;
522            }
523            crate::Expression::Override(r#override) => {
524                validate_override(r#override)?;
525            }
526            crate::Expression::ZeroValue(ty) => {
527                validate_type(ty)?;
528            }
529            crate::Expression::Compose { ty, ref components } => {
530                validate_type(ty)?;
531                handle.check_dep_iter(components.iter().copied())?;
532            }
533            crate::Expression::FunctionArgument(_arg_idx) => (),
534            crate::Expression::GlobalVariable(global_variable) => {
535                global_variable.check_valid_for(global_variables)?;
536            }
537            crate::Expression::LocalVariable(local_variable) => {
538                local_variable.check_valid_for(local_variables)?;
539            }
540            crate::Expression::Load { pointer } => {
541                handle.check_dep(pointer)?;
542            }
543            crate::Expression::ImageSample {
544                image,
545                sampler,
546                gather: _,
547                coordinate,
548                array_index,
549                offset,
550                level,
551                depth_ref,
552                clamp_to_edge: _,
553            } => {
554                handle
555                    .check_dep(image)?
556                    .check_dep(sampler)?
557                    .check_dep(coordinate)?
558                    .check_dep_opt(array_index)?
559                    .check_dep_opt(offset)?;
560
561                match level {
562                    crate::SampleLevel::Auto | crate::SampleLevel::Zero => (),
563                    crate::SampleLevel::Exact(expr) => {
564                        handle.check_dep(expr)?;
565                    }
566                    crate::SampleLevel::Bias(expr) => {
567                        handle.check_dep(expr)?;
568                    }
569                    crate::SampleLevel::Gradient { x, y } => {
570                        handle.check_dep(x)?.check_dep(y)?;
571                    }
572                };
573
574                handle.check_dep_opt(depth_ref)?;
575            }
576            crate::Expression::ImageLoad {
577                image,
578                coordinate,
579                array_index,
580                sample,
581                level,
582            } => {
583                handle
584                    .check_dep(image)?
585                    .check_dep(coordinate)?
586                    .check_dep_opt(array_index)?
587                    .check_dep_opt(sample)?
588                    .check_dep_opt(level)?;
589            }
590            crate::Expression::ImageQuery { image, query } => {
591                handle.check_dep(image)?;
592                match query {
593                    crate::ImageQuery::Size { level } => {
594                        handle.check_dep_opt(level)?;
595                    }
596                    crate::ImageQuery::NumLevels
597                    | crate::ImageQuery::NumLayers
598                    | crate::ImageQuery::NumSamples => (),
599                };
600            }
601            crate::Expression::Unary {
602                op: _,
603                expr: operand,
604            } => {
605                handle.check_dep(operand)?;
606            }
607            crate::Expression::Binary { op: _, left, right } => {
608                handle.check_dep(left)?.check_dep(right)?;
609            }
610            crate::Expression::Select {
611                condition,
612                accept,
613                reject,
614            } => {
615                handle
616                    .check_dep(condition)?
617                    .check_dep(accept)?
618                    .check_dep(reject)?;
619            }
620            crate::Expression::Derivative { expr: argument, .. } => {
621                handle.check_dep(argument)?;
622            }
623            crate::Expression::Relational { fun: _, argument } => {
624                handle.check_dep(argument)?;
625            }
626            crate::Expression::Math {
627                fun: _,
628                arg,
629                arg1,
630                arg2,
631                arg3,
632            } => {
633                handle
634                    .check_dep(arg)?
635                    .check_dep_opt(arg1)?
636                    .check_dep_opt(arg2)?
637                    .check_dep_opt(arg3)?;
638            }
639            crate::Expression::As {
640                expr: input,
641                kind: _,
642                convert: _,
643            } => {
644                handle.check_dep(input)?;
645            }
646            crate::Expression::CallResult(function) => {
647                Self::validate_function_handle(function, functions)?;
648                if let Some(handle) = current_function {
649                    handle.check_dep(function)?;
650                }
651            }
652            crate::Expression::AtomicResult { .. }
653            | crate::Expression::RayQueryProceedResult
654            | crate::Expression::SubgroupBallotResult
655            | crate::Expression::SubgroupOperationResult { .. }
656            | crate::Expression::WorkGroupUniformLoadResult { .. } => (),
657            crate::Expression::ArrayLength(array) => {
658                handle.check_dep(array)?;
659            }
660            crate::Expression::RayQueryGetIntersection {
661                query,
662                committed: _,
663            }
664            | crate::Expression::RayQueryVertexPositions {
665                query,
666                committed: _,
667            } => {
668                handle.check_dep(query)?;
669            }
670            crate::Expression::CooperativeLoad { ref data, .. } => {
671                handle.check_dep(data.pointer)?.check_dep(data.stride)?;
672            }
673            crate::Expression::CooperativeMultiplyAdd { a, b, c } => {
674                handle.check_dep(a)?.check_dep(b)?.check_dep(c)?;
675            }
676        }
677        Ok(())
678    }
679
680    fn validate_block_handles(
681        block: &crate::Block,
682        expressions: &Arena<crate::Expression>,
683        functions: &Arena<crate::Function>,
684    ) -> Result<(), InvalidHandleError> {
685        let validate_block = |block| Self::validate_block_handles(block, expressions, functions);
686        let validate_expr = |handle| Self::validate_expression_handle(handle, expressions);
687        let validate_expr_opt = |handle_opt| {
688            if let Some(handle) = handle_opt {
689                validate_expr(handle)?;
690            }
691            Ok(())
692        };
693
694        block.iter().try_for_each(|stmt| match *stmt {
695            crate::Statement::Emit(ref expr_range) => {
696                expr_range.check_valid_for(expressions)?;
697                Ok(())
698            }
699            crate::Statement::Block(ref block) => {
700                validate_block(block)?;
701                Ok(())
702            }
703            crate::Statement::If {
704                condition,
705                ref accept,
706                ref reject,
707            } => {
708                validate_expr(condition)?;
709                validate_block(accept)?;
710                validate_block(reject)?;
711                Ok(())
712            }
713            crate::Statement::Switch {
714                selector,
715                ref cases,
716            } => {
717                validate_expr(selector)?;
718                for &crate::SwitchCase {
719                    value: _,
720                    ref body,
721                    fall_through: _,
722                } in cases
723                {
724                    validate_block(body)?;
725                }
726                Ok(())
727            }
728            crate::Statement::Loop {
729                ref body,
730                ref continuing,
731                break_if,
732            } => {
733                validate_block(body)?;
734                validate_block(continuing)?;
735                validate_expr_opt(break_if)?;
736                Ok(())
737            }
738            crate::Statement::Return { value } => validate_expr_opt(value),
739            crate::Statement::Store { pointer, value } => {
740                validate_expr(pointer)?;
741                validate_expr(value)?;
742                Ok(())
743            }
744            crate::Statement::ImageStore {
745                image,
746                coordinate,
747                array_index,
748                value,
749            } => {
750                validate_expr(image)?;
751                validate_expr(coordinate)?;
752                validate_expr_opt(array_index)?;
753                validate_expr(value)?;
754                Ok(())
755            }
756            crate::Statement::Atomic {
757                pointer,
758                fun,
759                value,
760                result,
761            } => {
762                validate_expr(pointer)?;
763                match fun {
764                    crate::AtomicFunction::Add
765                    | crate::AtomicFunction::Subtract
766                    | crate::AtomicFunction::And
767                    | crate::AtomicFunction::ExclusiveOr
768                    | crate::AtomicFunction::InclusiveOr
769                    | crate::AtomicFunction::Min
770                    | crate::AtomicFunction::Max => (),
771                    crate::AtomicFunction::Exchange { compare } => validate_expr_opt(compare)?,
772                };
773                validate_expr(value)?;
774                if let Some(result) = result {
775                    validate_expr(result)?;
776                }
777                Ok(())
778            }
779            crate::Statement::ImageAtomic {
780                image,
781                coordinate,
782                array_index,
783                fun: _,
784                value,
785            } => {
786                validate_expr(image)?;
787                validate_expr(coordinate)?;
788                validate_expr_opt(array_index)?;
789                validate_expr(value)?;
790                Ok(())
791            }
792            crate::Statement::WorkGroupUniformLoad { pointer, result } => {
793                validate_expr(pointer)?;
794                validate_expr(result)?;
795                Ok(())
796            }
797            crate::Statement::Call {
798                function,
799                ref arguments,
800                result,
801            } => {
802                Self::validate_function_handle(function, functions)?;
803                for arg in arguments.iter().copied() {
804                    validate_expr(arg)?;
805                }
806                validate_expr_opt(result)?;
807                Ok(())
808            }
809            crate::Statement::RayQuery { query, ref fun } => {
810                validate_expr(query)?;
811                match *fun {
812                    crate::RayQueryFunction::Initialize {
813                        acceleration_structure,
814                        descriptor,
815                    } => {
816                        validate_expr(acceleration_structure)?;
817                        validate_expr(descriptor)?;
818                    }
819                    crate::RayQueryFunction::Proceed { result } => {
820                        validate_expr(result)?;
821                    }
822                    crate::RayQueryFunction::GenerateIntersection { hit_t } => {
823                        validate_expr(hit_t)?;
824                    }
825                    crate::RayQueryFunction::ConfirmIntersection => {}
826                    crate::RayQueryFunction::Terminate => {}
827                    crate::RayQueryFunction::Begin => {}
828                }
829                Ok(())
830            }
831            crate::Statement::SubgroupBallot { result, predicate } => {
832                validate_expr_opt(predicate)?;
833                validate_expr(result)?;
834                Ok(())
835            }
836            crate::Statement::SubgroupCollectiveOperation {
837                op: _,
838                collective_op: _,
839                argument,
840                result,
841            } => {
842                validate_expr(argument)?;
843                validate_expr(result)?;
844                Ok(())
845            }
846            crate::Statement::SubgroupGather {
847                mode,
848                argument,
849                result,
850            } => {
851                validate_expr(argument)?;
852                match mode {
853                    crate::GatherMode::BroadcastFirst => {}
854                    crate::GatherMode::Broadcast(index)
855                    | crate::GatherMode::Shuffle(index)
856                    | crate::GatherMode::ShuffleDown(index)
857                    | crate::GatherMode::ShuffleUp(index)
858                    | crate::GatherMode::ShuffleXor(index)
859                    | crate::GatherMode::QuadBroadcast(index) => validate_expr(index)?,
860                    crate::GatherMode::QuadSwap(_) => {}
861                }
862                validate_expr(result)?;
863                Ok(())
864            }
865            crate::Statement::CooperativeStore { target, ref data } => {
866                validate_expr(target)?;
867                validate_expr(data.pointer)?;
868                validate_expr(data.stride)?;
869                Ok(())
870            }
871            crate::Statement::RayPipelineFunction(fun) => match fun {
872                crate::RayPipelineFunction::TraceRay {
873                    acceleration_structure,
874                    descriptor,
875                    payload,
876                } => {
877                    validate_expr(acceleration_structure)?;
878                    validate_expr(descriptor)?;
879                    validate_expr(payload)?;
880                    Ok(())
881                }
882            },
883            crate::Statement::DebugPrintf {
884                format: _,
885                ref arguments,
886            } => {
887                for &arg in arguments {
888                    validate_expr(arg)?;
889                }
890                Ok(())
891            }
892            crate::Statement::Break
893            | crate::Statement::Continue
894            | crate::Statement::Kill
895            | crate::Statement::ControlBarrier(_)
896            | crate::Statement::MemoryBarrier(_) => Ok(()),
897        })
898    }
899}
900
901impl From<BadHandle> for Box<ValidationError> {
902    fn from(source: BadHandle) -> Self {
903        Box::new(ValidationError::InvalidHandle(source.into()))
904    }
905}
906
907impl From<FwdDepError> for Box<ValidationError> {
908    fn from(source: FwdDepError) -> Self {
909        Box::new(ValidationError::InvalidHandle(source.into()))
910    }
911}
912
913impl From<BadRangeError> for Box<ValidationError> {
914    fn from(source: BadRangeError) -> Self {
915        Box::new(ValidationError::InvalidHandle(source.into()))
916    }
917}
918
919impl From<InvalidHandleError> for Box<ValidationError> {
920    fn from(source: InvalidHandleError) -> Self {
921        Box::new(ValidationError::InvalidHandle(source))
922    }
923}
924
925#[derive(Clone, Debug, thiserror::Error)]
926#[cfg_attr(test, derive(PartialEq))]
927pub enum InvalidHandleError {
928    #[error(transparent)]
929    BadHandle(#[from] BadHandle),
930    #[error(transparent)]
931    ForwardDependency(#[from] FwdDepError),
932    #[error(transparent)]
933    BadRange(#[from] BadRangeError),
934}
935
936#[derive(Clone, Debug, thiserror::Error)]
937#[cfg_attr(test, derive(PartialEq))]
938#[error(
939    "{subject:?} of kind {subject_kind:?} depends on {depends_on:?} of kind {depends_on_kind}, \
940    which has not been processed yet"
941)]
942pub struct FwdDepError {
943    // This error is used for many `Handle` types, but there's no point in making this generic, so
944    // we just flatten them all to `Handle<()>` here.
945    subject: Handle<()>,
946    subject_kind: &'static str,
947    depends_on: Handle<()>,
948    depends_on_kind: &'static str,
949}
950
951impl<T> Handle<T> {
952    /// Check that `self` is valid within `arena` using [`Arena::check_contains_handle`].
953    pub(self) fn check_valid_for(self, arena: &Arena<T>) -> Result<(), InvalidHandleError> {
954        arena.check_contains_handle(self)?;
955        Ok(())
956    }
957
958    /// Check that `self` is valid within `arena` using [`UniqueArena::check_contains_handle`].
959    pub(self) fn check_valid_for_uniq(
960        self,
961        arena: &UniqueArena<T>,
962    ) -> Result<(), InvalidHandleError>
963    where
964        T: Eq + Hash,
965    {
966        arena.check_contains_handle(self)?;
967        Ok(())
968    }
969
970    /// Check that `depends_on` was constructed before `self` by comparing handle indices.
971    ///
972    /// If `self` is a valid handle (i.e., it has been validated using [`Self::check_valid_for`])
973    /// and this function returns [`Ok`], then it may be assumed that `depends_on` is also valid.
974    /// In [`naga`](crate)'s current arena-based implementation, this is useful for validating
975    /// recursive definitions of arena-based values in linear time.
976    ///
977    /// # Errors
978    ///
979    /// If `depends_on`'s handle is from the same [`Arena`] as `self'`s, but not constructed earlier
980    /// than `self`'s, this function returns an error.
981    pub(self) fn check_dep(self, depends_on: Self) -> Result<Self, FwdDepError> {
982        if depends_on < self {
983            Ok(self)
984        } else {
985            let erase_handle_type = |handle: Handle<_>| {
986                Handle::new(NonMaxU32::new((handle.index()).try_into().unwrap()).unwrap())
987            };
988            Err(FwdDepError {
989                subject: erase_handle_type(self),
990                subject_kind: core::any::type_name::<T>(),
991                depends_on: erase_handle_type(depends_on),
992                depends_on_kind: core::any::type_name::<T>(),
993            })
994        }
995    }
996
997    /// Like [`Self::check_dep`], except for [`Option`]al handle values.
998    pub(self) fn check_dep_opt(self, depends_on: Option<Self>) -> Result<Self, FwdDepError> {
999        self.check_dep_iter(depends_on.into_iter())
1000    }
1001
1002    /// Like [`Self::check_dep`], except for [`Iterator`]s over handle values.
1003    pub(self) fn check_dep_iter(
1004        self,
1005        depends_on: impl Iterator<Item = Self>,
1006    ) -> Result<Self, FwdDepError> {
1007        for handle in depends_on {
1008            self.check_dep(handle)?;
1009        }
1010        Ok(self)
1011    }
1012}
1013
1014impl<T> crate::arena::Range<T> {
1015    pub(self) fn check_valid_for(&self, arena: &Arena<T>) -> Result<(), BadRangeError> {
1016        arena.check_contains_range(self)
1017    }
1018}
1019
1020#[test]
1021fn constant_deps() {
1022    use crate::{Constant, Expression, Literal, Span, Type, TypeInner};
1023
1024    let nowhere = Span::default();
1025
1026    let mut types = UniqueArena::new();
1027    let mut const_exprs = Arena::new();
1028    let mut fun_exprs = Arena::new();
1029    let mut constants = Arena::new();
1030    let overrides = Arena::new();
1031
1032    let i32_handle = types.insert(
1033        Type {
1034            name: None,
1035            inner: TypeInner::Scalar(crate::Scalar::I32),
1036        },
1037        nowhere,
1038    );
1039
1040    // Construct a self-referential constant by misusing a handle to
1041    // fun_exprs as a constant initializer.
1042    let fun_expr = fun_exprs.append(Expression::Literal(Literal::I32(42)), nowhere);
1043    let self_referential_const = constants.append(
1044        Constant {
1045            name: None,
1046            ty: i32_handle,
1047            init: fun_expr,
1048        },
1049        nowhere,
1050    );
1051    let _self_referential_expr =
1052        const_exprs.append(Expression::Constant(self_referential_const), nowhere);
1053
1054    for handle_and_expr in const_exprs.iter() {
1055        assert!(super::Validator::validate_const_expression_handles(
1056            handle_and_expr,
1057            &constants,
1058            &overrides,
1059        )
1060        .is_err());
1061    }
1062}
1063
1064#[test]
1065fn array_size_deps() {
1066    use super::Validator;
1067    use crate::{ArraySize, Expression, Override, Scalar, Span, Type, TypeInner};
1068
1069    let nowhere = Span::default();
1070
1071    let mut m = crate::Module::default();
1072
1073    let ty_u32 = m.types.insert(
1074        Type {
1075            name: Some("u32".to_string()),
1076            inner: TypeInner::Scalar(Scalar::U32),
1077        },
1078        nowhere,
1079    );
1080    let ex_zero = m
1081        .global_expressions
1082        .append(Expression::ZeroValue(ty_u32), nowhere);
1083    let ty_handle = m.overrides.append(
1084        Override {
1085            name: None,
1086            id: None,
1087            ty: ty_u32,
1088            init: Some(ex_zero),
1089        },
1090        nowhere,
1091    );
1092    let ty_arr = m.types.insert(
1093        Type {
1094            name: Some("bad_array".to_string()),
1095            inner: TypeInner::Array {
1096                base: ty_u32,
1097                size: ArraySize::Pending(ty_handle),
1098                stride: 4,
1099            },
1100        },
1101        nowhere,
1102    );
1103
1104    // Everything should be okay now.
1105    assert!(Validator::validate_module_handles(&m).is_ok());
1106
1107    // Mutate `ex_zero`'s type to `ty_arr`, introducing a cycle.
1108    // Validation should catch the cycle.
1109    m.global_expressions[ex_zero] = Expression::ZeroValue(ty_arr);
1110    assert!(Validator::validate_module_handles(&m).is_err());
1111}
1112
1113#[test]
1114fn array_size_override() {
1115    use super::Validator;
1116    use crate::{ArraySize, Override, Scalar, Span, Type, TypeInner};
1117
1118    let nowhere = Span::default();
1119
1120    let mut m = crate::Module::default();
1121
1122    let ty_u32 = m.types.insert(
1123        Type {
1124            name: Some("u32".to_string()),
1125            inner: TypeInner::Scalar(Scalar::U32),
1126        },
1127        nowhere,
1128    );
1129
1130    let bad_override: Handle<Override> = Handle::new(NonMaxU32::new(1000).unwrap());
1131    let _ty_arr = m.types.insert(
1132        Type {
1133            name: Some("bad_array".to_string()),
1134            inner: TypeInner::Array {
1135                base: ty_u32,
1136                size: ArraySize::Pending(bad_override),
1137                stride: 4,
1138            },
1139        },
1140        nowhere,
1141    );
1142
1143    assert!(Validator::validate_module_handles(&m).is_err());
1144}
1145
1146#[test]
1147fn override_init_deps() {
1148    use super::Validator;
1149    use crate::{ArraySize, Expression, Override, Scalar, Span, Type, TypeInner};
1150
1151    let nowhere = Span::default();
1152
1153    let mut m = crate::Module::default();
1154
1155    let ty_u32 = m.types.insert(
1156        Type {
1157            name: Some("u32".to_string()),
1158            inner: TypeInner::Scalar(Scalar::U32),
1159        },
1160        nowhere,
1161    );
1162    let ex_zero = m
1163        .global_expressions
1164        .append(Expression::ZeroValue(ty_u32), nowhere);
1165    let r#override = m.overrides.append(
1166        Override {
1167            name: Some("bad_override".into()),
1168            id: None,
1169            ty: ty_u32,
1170            init: Some(ex_zero),
1171        },
1172        nowhere,
1173    );
1174    let ty_arr = m.types.insert(
1175        Type {
1176            name: Some("bad_array".to_string()),
1177            inner: TypeInner::Array {
1178                base: ty_u32,
1179                size: ArraySize::Pending(r#override),
1180                stride: 4,
1181            },
1182        },
1183        nowhere,
1184    );
1185    let ex_arr = m
1186        .global_expressions
1187        .append(Expression::ZeroValue(ty_arr), nowhere);
1188
1189    assert!(Validator::validate_module_handles(&m).is_ok());
1190
1191    // Mutate `r#override`'s initializer to `ex_arr`, introducing a cycle.
1192    // Validation should catch the cycle.
1193    m.overrides[r#override].init = Some(ex_arr);
1194    assert!(Validator::validate_module_handles(&m).is_err());
1195}