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                }
828                Ok(())
829            }
830            crate::Statement::SubgroupBallot { result, predicate } => {
831                validate_expr_opt(predicate)?;
832                validate_expr(result)?;
833                Ok(())
834            }
835            crate::Statement::SubgroupCollectiveOperation {
836                op: _,
837                collective_op: _,
838                argument,
839                result,
840            } => {
841                validate_expr(argument)?;
842                validate_expr(result)?;
843                Ok(())
844            }
845            crate::Statement::SubgroupGather {
846                mode,
847                argument,
848                result,
849            } => {
850                validate_expr(argument)?;
851                match mode {
852                    crate::GatherMode::BroadcastFirst => {}
853                    crate::GatherMode::Broadcast(index)
854                    | crate::GatherMode::Shuffle(index)
855                    | crate::GatherMode::ShuffleDown(index)
856                    | crate::GatherMode::ShuffleUp(index)
857                    | crate::GatherMode::ShuffleXor(index)
858                    | crate::GatherMode::QuadBroadcast(index) => validate_expr(index)?,
859                    crate::GatherMode::QuadSwap(_) => {}
860                }
861                validate_expr(result)?;
862                Ok(())
863            }
864            crate::Statement::CooperativeStore { target, ref data } => {
865                validate_expr(target)?;
866                validate_expr(data.pointer)?;
867                validate_expr(data.stride)?;
868                Ok(())
869            }
870            crate::Statement::RayPipelineFunction(fun) => match fun {
871                crate::RayPipelineFunction::TraceRay {
872                    acceleration_structure,
873                    descriptor,
874                    payload,
875                } => {
876                    validate_expr(acceleration_structure)?;
877                    validate_expr(descriptor)?;
878                    validate_expr(payload)?;
879                    Ok(())
880                }
881            },
882            crate::Statement::Break
883            | crate::Statement::Continue
884            | crate::Statement::Kill
885            | crate::Statement::ControlBarrier(_)
886            | crate::Statement::MemoryBarrier(_) => Ok(()),
887        })
888    }
889}
890
891impl From<BadHandle> for Box<ValidationError> {
892    fn from(source: BadHandle) -> Self {
893        Box::new(ValidationError::InvalidHandle(source.into()))
894    }
895}
896
897impl From<FwdDepError> for Box<ValidationError> {
898    fn from(source: FwdDepError) -> Self {
899        Box::new(ValidationError::InvalidHandle(source.into()))
900    }
901}
902
903impl From<BadRangeError> for Box<ValidationError> {
904    fn from(source: BadRangeError) -> Self {
905        Box::new(ValidationError::InvalidHandle(source.into()))
906    }
907}
908
909impl From<InvalidHandleError> for Box<ValidationError> {
910    fn from(source: InvalidHandleError) -> Self {
911        Box::new(ValidationError::InvalidHandle(source))
912    }
913}
914
915#[derive(Clone, Debug, thiserror::Error)]
916#[cfg_attr(test, derive(PartialEq))]
917pub enum InvalidHandleError {
918    #[error(transparent)]
919    BadHandle(#[from] BadHandle),
920    #[error(transparent)]
921    ForwardDependency(#[from] FwdDepError),
922    #[error(transparent)]
923    BadRange(#[from] BadRangeError),
924}
925
926#[derive(Clone, Debug, thiserror::Error)]
927#[cfg_attr(test, derive(PartialEq))]
928#[error(
929    "{subject:?} of kind {subject_kind:?} depends on {depends_on:?} of kind {depends_on_kind}, \
930    which has not been processed yet"
931)]
932pub struct FwdDepError {
933    // This error is used for many `Handle` types, but there's no point in making this generic, so
934    // we just flatten them all to `Handle<()>` here.
935    subject: Handle<()>,
936    subject_kind: &'static str,
937    depends_on: Handle<()>,
938    depends_on_kind: &'static str,
939}
940
941impl<T> Handle<T> {
942    /// Check that `self` is valid within `arena` using [`Arena::check_contains_handle`].
943    pub(self) fn check_valid_for(self, arena: &Arena<T>) -> Result<(), InvalidHandleError> {
944        arena.check_contains_handle(self)?;
945        Ok(())
946    }
947
948    /// Check that `self` is valid within `arena` using [`UniqueArena::check_contains_handle`].
949    pub(self) fn check_valid_for_uniq(
950        self,
951        arena: &UniqueArena<T>,
952    ) -> Result<(), InvalidHandleError>
953    where
954        T: Eq + Hash,
955    {
956        arena.check_contains_handle(self)?;
957        Ok(())
958    }
959
960    /// Check that `depends_on` was constructed before `self` by comparing handle indices.
961    ///
962    /// If `self` is a valid handle (i.e., it has been validated using [`Self::check_valid_for`])
963    /// and this function returns [`Ok`], then it may be assumed that `depends_on` is also valid.
964    /// In [`naga`](crate)'s current arena-based implementation, this is useful for validating
965    /// recursive definitions of arena-based values in linear time.
966    ///
967    /// # Errors
968    ///
969    /// If `depends_on`'s handle is from the same [`Arena`] as `self'`s, but not constructed earlier
970    /// than `self`'s, this function returns an error.
971    pub(self) fn check_dep(self, depends_on: Self) -> Result<Self, FwdDepError> {
972        if depends_on < self {
973            Ok(self)
974        } else {
975            let erase_handle_type = |handle: Handle<_>| {
976                Handle::new(NonMaxU32::new((handle.index()).try_into().unwrap()).unwrap())
977            };
978            Err(FwdDepError {
979                subject: erase_handle_type(self),
980                subject_kind: core::any::type_name::<T>(),
981                depends_on: erase_handle_type(depends_on),
982                depends_on_kind: core::any::type_name::<T>(),
983            })
984        }
985    }
986
987    /// Like [`Self::check_dep`], except for [`Option`]al handle values.
988    pub(self) fn check_dep_opt(self, depends_on: Option<Self>) -> Result<Self, FwdDepError> {
989        self.check_dep_iter(depends_on.into_iter())
990    }
991
992    /// Like [`Self::check_dep`], except for [`Iterator`]s over handle values.
993    pub(self) fn check_dep_iter(
994        self,
995        depends_on: impl Iterator<Item = Self>,
996    ) -> Result<Self, FwdDepError> {
997        for handle in depends_on {
998            self.check_dep(handle)?;
999        }
1000        Ok(self)
1001    }
1002}
1003
1004impl<T> crate::arena::Range<T> {
1005    pub(self) fn check_valid_for(&self, arena: &Arena<T>) -> Result<(), BadRangeError> {
1006        arena.check_contains_range(self)
1007    }
1008}
1009
1010#[test]
1011fn constant_deps() {
1012    use crate::{Constant, Expression, Literal, Span, Type, TypeInner};
1013
1014    let nowhere = Span::default();
1015
1016    let mut types = UniqueArena::new();
1017    let mut const_exprs = Arena::new();
1018    let mut fun_exprs = Arena::new();
1019    let mut constants = Arena::new();
1020    let overrides = Arena::new();
1021
1022    let i32_handle = types.insert(
1023        Type {
1024            name: None,
1025            inner: TypeInner::Scalar(crate::Scalar::I32),
1026        },
1027        nowhere,
1028    );
1029
1030    // Construct a self-referential constant by misusing a handle to
1031    // fun_exprs as a constant initializer.
1032    let fun_expr = fun_exprs.append(Expression::Literal(Literal::I32(42)), nowhere);
1033    let self_referential_const = constants.append(
1034        Constant {
1035            name: None,
1036            ty: i32_handle,
1037            init: fun_expr,
1038        },
1039        nowhere,
1040    );
1041    let _self_referential_expr =
1042        const_exprs.append(Expression::Constant(self_referential_const), nowhere);
1043
1044    for handle_and_expr in const_exprs.iter() {
1045        assert!(super::Validator::validate_const_expression_handles(
1046            handle_and_expr,
1047            &constants,
1048            &overrides,
1049        )
1050        .is_err());
1051    }
1052}
1053
1054#[test]
1055fn array_size_deps() {
1056    use super::Validator;
1057    use crate::{ArraySize, Expression, Override, Scalar, Span, Type, TypeInner};
1058
1059    let nowhere = Span::default();
1060
1061    let mut m = crate::Module::default();
1062
1063    let ty_u32 = m.types.insert(
1064        Type {
1065            name: Some("u32".to_string()),
1066            inner: TypeInner::Scalar(Scalar::U32),
1067        },
1068        nowhere,
1069    );
1070    let ex_zero = m
1071        .global_expressions
1072        .append(Expression::ZeroValue(ty_u32), nowhere);
1073    let ty_handle = m.overrides.append(
1074        Override {
1075            name: None,
1076            id: None,
1077            ty: ty_u32,
1078            init: Some(ex_zero),
1079        },
1080        nowhere,
1081    );
1082    let ty_arr = m.types.insert(
1083        Type {
1084            name: Some("bad_array".to_string()),
1085            inner: TypeInner::Array {
1086                base: ty_u32,
1087                size: ArraySize::Pending(ty_handle),
1088                stride: 4,
1089            },
1090        },
1091        nowhere,
1092    );
1093
1094    // Everything should be okay now.
1095    assert!(Validator::validate_module_handles(&m).is_ok());
1096
1097    // Mutate `ex_zero`'s type to `ty_arr`, introducing a cycle.
1098    // Validation should catch the cycle.
1099    m.global_expressions[ex_zero] = Expression::ZeroValue(ty_arr);
1100    assert!(Validator::validate_module_handles(&m).is_err());
1101}
1102
1103#[test]
1104fn array_size_override() {
1105    use super::Validator;
1106    use crate::{ArraySize, Override, Scalar, Span, Type, TypeInner};
1107
1108    let nowhere = Span::default();
1109
1110    let mut m = crate::Module::default();
1111
1112    let ty_u32 = m.types.insert(
1113        Type {
1114            name: Some("u32".to_string()),
1115            inner: TypeInner::Scalar(Scalar::U32),
1116        },
1117        nowhere,
1118    );
1119
1120    let bad_override: Handle<Override> = Handle::new(NonMaxU32::new(1000).unwrap());
1121    let _ty_arr = m.types.insert(
1122        Type {
1123            name: Some("bad_array".to_string()),
1124            inner: TypeInner::Array {
1125                base: ty_u32,
1126                size: ArraySize::Pending(bad_override),
1127                stride: 4,
1128            },
1129        },
1130        nowhere,
1131    );
1132
1133    assert!(Validator::validate_module_handles(&m).is_err());
1134}
1135
1136#[test]
1137fn override_init_deps() {
1138    use super::Validator;
1139    use crate::{ArraySize, Expression, Override, Scalar, Span, Type, TypeInner};
1140
1141    let nowhere = Span::default();
1142
1143    let mut m = crate::Module::default();
1144
1145    let ty_u32 = m.types.insert(
1146        Type {
1147            name: Some("u32".to_string()),
1148            inner: TypeInner::Scalar(Scalar::U32),
1149        },
1150        nowhere,
1151    );
1152    let ex_zero = m
1153        .global_expressions
1154        .append(Expression::ZeroValue(ty_u32), nowhere);
1155    let r#override = m.overrides.append(
1156        Override {
1157            name: Some("bad_override".into()),
1158            id: None,
1159            ty: ty_u32,
1160            init: Some(ex_zero),
1161        },
1162        nowhere,
1163    );
1164    let ty_arr = m.types.insert(
1165        Type {
1166            name: Some("bad_array".to_string()),
1167            inner: TypeInner::Array {
1168                base: ty_u32,
1169                size: ArraySize::Pending(r#override),
1170                stride: 4,
1171            },
1172        },
1173        nowhere,
1174    );
1175    let ex_arr = m
1176        .global_expressions
1177        .append(Expression::ZeroValue(ty_arr), nowhere);
1178
1179    assert!(Validator::validate_module_handles(&m).is_ok());
1180
1181    // Mutate `r#override`'s initializer to `ex_arr`, introducing a cycle.
1182    // Validation should catch the cycle.
1183    m.overrides[r#override].init = Some(ex_arr);
1184    assert!(Validator::validate_module_handles(&m).is_err());
1185}