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            crate::Expression::Bitcast { expr, ty } => {
486                handle.check_dep(expr)?;
487                Some(ty)
488            }
489            _ => None,
490        };
491        Ok(max_type)
492    }
493
494    #[allow(clippy::too_many_arguments)]
495    fn validate_expression_handles(
496        (handle, expression): (Handle<crate::Expression>, &crate::Expression),
497        constants: &Arena<crate::Constant>,
498        overrides: &Arena<crate::Override>,
499        types: &UniqueArena<crate::Type>,
500        local_variables: &Arena<crate::LocalVariable>,
501        global_variables: &Arena<crate::GlobalVariable>,
502        functions: &Arena<crate::Function>,
503        // The handle of the current function or `None` if it's an entry point
504        current_function: Option<Handle<crate::Function>>,
505    ) -> Result<(), InvalidHandleError> {
506        let validate_constant = |handle| Self::validate_constant_handle(handle, constants);
507        let validate_override = |handle| Self::validate_override_handle(handle, overrides);
508        let validate_type = |handle| Self::validate_type_handle(handle, types);
509
510        match *expression {
511            crate::Expression::Access { base, index } => {
512                handle.check_dep(base)?.check_dep(index)?;
513            }
514            crate::Expression::AccessIndex { base, .. } => {
515                handle.check_dep(base)?;
516            }
517            crate::Expression::Splat { value, .. } => {
518                handle.check_dep(value)?;
519            }
520            crate::Expression::Swizzle { vector, .. } => {
521                handle.check_dep(vector)?;
522            }
523            crate::Expression::Literal(_) => {}
524            crate::Expression::Constant(constant) => {
525                validate_constant(constant)?;
526            }
527            crate::Expression::Override(r#override) => {
528                validate_override(r#override)?;
529            }
530            crate::Expression::ZeroValue(ty) => {
531                validate_type(ty)?;
532            }
533            crate::Expression::Compose { ty, ref components } => {
534                validate_type(ty)?;
535                handle.check_dep_iter(components.iter().copied())?;
536            }
537            crate::Expression::FunctionArgument(_arg_idx) => (),
538            crate::Expression::GlobalVariable(global_variable) => {
539                global_variable.check_valid_for(global_variables)?;
540            }
541            crate::Expression::LocalVariable(local_variable) => {
542                local_variable.check_valid_for(local_variables)?;
543            }
544            crate::Expression::Load { pointer } => {
545                handle.check_dep(pointer)?;
546            }
547            crate::Expression::ImageSample {
548                image,
549                sampler,
550                gather: _,
551                coordinate,
552                array_index,
553                offset,
554                level,
555                depth_ref,
556                clamp_to_edge: _,
557            } => {
558                handle
559                    .check_dep(image)?
560                    .check_dep(sampler)?
561                    .check_dep(coordinate)?
562                    .check_dep_opt(array_index)?
563                    .check_dep_opt(offset)?;
564
565                match level {
566                    crate::SampleLevel::Auto | crate::SampleLevel::Zero => (),
567                    crate::SampleLevel::Exact(expr) => {
568                        handle.check_dep(expr)?;
569                    }
570                    crate::SampleLevel::Bias(expr) => {
571                        handle.check_dep(expr)?;
572                    }
573                    crate::SampleLevel::Gradient { x, y } => {
574                        handle.check_dep(x)?.check_dep(y)?;
575                    }
576                };
577
578                handle.check_dep_opt(depth_ref)?;
579            }
580            crate::Expression::ImageLoad {
581                image,
582                coordinate,
583                array_index,
584                sample,
585                level,
586            } => {
587                handle
588                    .check_dep(image)?
589                    .check_dep(coordinate)?
590                    .check_dep_opt(array_index)?
591                    .check_dep_opt(sample)?
592                    .check_dep_opt(level)?;
593            }
594            crate::Expression::ImageQuery { image, query } => {
595                handle.check_dep(image)?;
596                match query {
597                    crate::ImageQuery::Size { level } => {
598                        handle.check_dep_opt(level)?;
599                    }
600                    crate::ImageQuery::NumLevels
601                    | crate::ImageQuery::NumLayers
602                    | crate::ImageQuery::NumSamples => (),
603                };
604            }
605            crate::Expression::Unary {
606                op: _,
607                expr: operand,
608            } => {
609                handle.check_dep(operand)?;
610            }
611            crate::Expression::Binary { op: _, left, right } => {
612                handle.check_dep(left)?.check_dep(right)?;
613            }
614            crate::Expression::Select {
615                condition,
616                accept,
617                reject,
618            } => {
619                handle
620                    .check_dep(condition)?
621                    .check_dep(accept)?
622                    .check_dep(reject)?;
623            }
624            crate::Expression::Derivative { expr: argument, .. } => {
625                handle.check_dep(argument)?;
626            }
627            crate::Expression::Relational { fun: _, argument } => {
628                handle.check_dep(argument)?;
629            }
630            crate::Expression::Math {
631                fun: _,
632                arg,
633                arg1,
634                arg2,
635                arg3,
636            } => {
637                handle
638                    .check_dep(arg)?
639                    .check_dep_opt(arg1)?
640                    .check_dep_opt(arg2)?
641                    .check_dep_opt(arg3)?;
642            }
643            crate::Expression::As {
644                expr: input,
645                target: _,
646            } => {
647                handle.check_dep(input)?;
648            }
649            crate::Expression::Bitcast { expr: input, ty } => {
650                validate_type(ty)?;
651                handle.check_dep(input)?;
652            }
653            crate::Expression::CallResult(function) => {
654                Self::validate_function_handle(function, functions)?;
655                if let Some(handle) = current_function {
656                    handle.check_dep(function)?;
657                }
658            }
659            crate::Expression::AtomicResult { .. }
660            | crate::Expression::RayQueryProceedResult
661            | crate::Expression::SubgroupBallotResult
662            | crate::Expression::SubgroupOperationResult { .. }
663            | crate::Expression::WorkGroupUniformLoadResult { .. } => (),
664            crate::Expression::ArrayLength(array) => {
665                handle.check_dep(array)?;
666            }
667            crate::Expression::RayQueryGetIntersection {
668                query,
669                committed: _,
670            }
671            | crate::Expression::RayQueryVertexPositions {
672                query,
673                committed: _,
674            } => {
675                handle.check_dep(query)?;
676            }
677            crate::Expression::CooperativeLoad { ref data, .. } => {
678                handle.check_dep(data.pointer)?.check_dep(data.stride)?;
679            }
680            crate::Expression::CooperativeMultiplyAdd { a, b, c } => {
681                handle.check_dep(a)?.check_dep(b)?.check_dep(c)?;
682            }
683        }
684        Ok(())
685    }
686
687    fn validate_block_handles(
688        block: &crate::Block,
689        expressions: &Arena<crate::Expression>,
690        functions: &Arena<crate::Function>,
691    ) -> Result<(), InvalidHandleError> {
692        let validate_block = |block| Self::validate_block_handles(block, expressions, functions);
693        let validate_expr = |handle| Self::validate_expression_handle(handle, expressions);
694        let validate_expr_opt = |handle_opt| {
695            if let Some(handle) = handle_opt {
696                validate_expr(handle)?;
697            }
698            Ok(())
699        };
700
701        block.iter().try_for_each(|stmt| match *stmt {
702            crate::Statement::Emit(ref expr_range) => {
703                expr_range.check_valid_for(expressions)?;
704                Ok(())
705            }
706            crate::Statement::Block(ref block) => {
707                validate_block(block)?;
708                Ok(())
709            }
710            crate::Statement::If {
711                condition,
712                ref accept,
713                ref reject,
714            } => {
715                validate_expr(condition)?;
716                validate_block(accept)?;
717                validate_block(reject)?;
718                Ok(())
719            }
720            crate::Statement::Switch {
721                selector,
722                ref cases,
723            } => {
724                validate_expr(selector)?;
725                for &crate::SwitchCase {
726                    value: _,
727                    ref body,
728                    fall_through: _,
729                } in cases
730                {
731                    validate_block(body)?;
732                }
733                Ok(())
734            }
735            crate::Statement::Loop {
736                ref body,
737                ref continuing,
738                break_if,
739            } => {
740                validate_block(body)?;
741                validate_block(continuing)?;
742                validate_expr_opt(break_if)?;
743                Ok(())
744            }
745            crate::Statement::Return { value } => validate_expr_opt(value),
746            crate::Statement::Store { pointer, value } => {
747                validate_expr(pointer)?;
748                validate_expr(value)?;
749                Ok(())
750            }
751            crate::Statement::ImageStore {
752                image,
753                coordinate,
754                array_index,
755                value,
756            } => {
757                validate_expr(image)?;
758                validate_expr(coordinate)?;
759                validate_expr_opt(array_index)?;
760                validate_expr(value)?;
761                Ok(())
762            }
763            crate::Statement::Atomic {
764                pointer,
765                fun,
766                value,
767                result,
768            } => {
769                validate_expr(pointer)?;
770                match fun {
771                    crate::AtomicFunction::Add
772                    | crate::AtomicFunction::Subtract
773                    | crate::AtomicFunction::And
774                    | crate::AtomicFunction::ExclusiveOr
775                    | crate::AtomicFunction::InclusiveOr
776                    | crate::AtomicFunction::Min
777                    | crate::AtomicFunction::Max => (),
778                    crate::AtomicFunction::Exchange { compare } => validate_expr_opt(compare)?,
779                };
780                validate_expr(value)?;
781                if let Some(result) = result {
782                    validate_expr(result)?;
783                }
784                Ok(())
785            }
786            crate::Statement::ImageAtomic {
787                image,
788                coordinate,
789                array_index,
790                fun: _,
791                value,
792            } => {
793                validate_expr(image)?;
794                validate_expr(coordinate)?;
795                validate_expr_opt(array_index)?;
796                validate_expr(value)?;
797                Ok(())
798            }
799            crate::Statement::WorkGroupUniformLoad { pointer, result } => {
800                validate_expr(pointer)?;
801                validate_expr(result)?;
802                Ok(())
803            }
804            crate::Statement::Call {
805                function,
806                ref arguments,
807                result,
808            } => {
809                Self::validate_function_handle(function, functions)?;
810                for arg in arguments.iter().copied() {
811                    validate_expr(arg)?;
812                }
813                validate_expr_opt(result)?;
814                Ok(())
815            }
816            crate::Statement::RayQuery { query, ref fun } => {
817                validate_expr(query)?;
818                match *fun {
819                    crate::RayQueryFunction::Initialize {
820                        acceleration_structure,
821                        descriptor,
822                    } => {
823                        validate_expr(acceleration_structure)?;
824                        validate_expr(descriptor)?;
825                    }
826                    crate::RayQueryFunction::Proceed { result } => {
827                        validate_expr(result)?;
828                    }
829                    crate::RayQueryFunction::GenerateIntersection { hit_t } => {
830                        validate_expr(hit_t)?;
831                    }
832                    crate::RayQueryFunction::ConfirmIntersection => {}
833                    crate::RayQueryFunction::Terminate => {}
834                    crate::RayQueryFunction::Begin => {}
835                }
836                Ok(())
837            }
838            crate::Statement::SubgroupBallot { result, predicate } => {
839                validate_expr_opt(predicate)?;
840                validate_expr(result)?;
841                Ok(())
842            }
843            crate::Statement::SubgroupCollectiveOperation {
844                op: _,
845                collective_op: _,
846                argument,
847                result,
848            } => {
849                validate_expr(argument)?;
850                validate_expr(result)?;
851                Ok(())
852            }
853            crate::Statement::SubgroupGather {
854                mode,
855                argument,
856                result,
857            } => {
858                validate_expr(argument)?;
859                match mode {
860                    crate::GatherMode::BroadcastFirst => {}
861                    crate::GatherMode::Broadcast(index)
862                    | crate::GatherMode::Shuffle(index)
863                    | crate::GatherMode::ShuffleDown(index)
864                    | crate::GatherMode::ShuffleUp(index)
865                    | crate::GatherMode::ShuffleXor(index)
866                    | crate::GatherMode::QuadBroadcast(index) => validate_expr(index)?,
867                    crate::GatherMode::QuadSwap(_) => {}
868                }
869                validate_expr(result)?;
870                Ok(())
871            }
872            crate::Statement::CooperativeStore { target, ref data } => {
873                validate_expr(target)?;
874                validate_expr(data.pointer)?;
875                validate_expr(data.stride)?;
876                Ok(())
877            }
878            crate::Statement::RayPipelineFunction(fun) => match fun {
879                crate::RayPipelineFunction::TraceRay {
880                    acceleration_structure,
881                    descriptor,
882                    payload,
883                } => {
884                    validate_expr(acceleration_structure)?;
885                    validate_expr(descriptor)?;
886                    validate_expr(payload)?;
887                    Ok(())
888                }
889            },
890            crate::Statement::DebugPrintf {
891                format: _,
892                ref arguments,
893            } => {
894                for &arg in arguments {
895                    validate_expr(arg)?;
896                }
897                Ok(())
898            }
899            crate::Statement::Break
900            | crate::Statement::Continue
901            | crate::Statement::Kill
902            | crate::Statement::ControlBarrier(_)
903            | crate::Statement::MemoryBarrier(_) => Ok(()),
904        })
905    }
906}
907
908impl From<BadHandle> for Box<ValidationError> {
909    fn from(source: BadHandle) -> Self {
910        Box::new(ValidationError::InvalidHandle(source.into()))
911    }
912}
913
914impl From<FwdDepError> for Box<ValidationError> {
915    fn from(source: FwdDepError) -> Self {
916        Box::new(ValidationError::InvalidHandle(source.into()))
917    }
918}
919
920impl From<BadRangeError> for Box<ValidationError> {
921    fn from(source: BadRangeError) -> Self {
922        Box::new(ValidationError::InvalidHandle(source.into()))
923    }
924}
925
926impl From<InvalidHandleError> for Box<ValidationError> {
927    fn from(source: InvalidHandleError) -> Self {
928        Box::new(ValidationError::InvalidHandle(source))
929    }
930}
931
932#[derive(Clone, Debug, thiserror::Error)]
933#[cfg_attr(test, derive(PartialEq))]
934pub enum InvalidHandleError {
935    #[error(transparent)]
936    BadHandle(#[from] BadHandle),
937    #[error(transparent)]
938    ForwardDependency(#[from] FwdDepError),
939    #[error(transparent)]
940    BadRange(#[from] BadRangeError),
941}
942
943#[derive(Clone, Debug, thiserror::Error)]
944#[cfg_attr(test, derive(PartialEq))]
945#[error(
946    "{subject:?} of kind {subject_kind:?} depends on {depends_on:?} of kind {depends_on_kind}, \
947    which has not been processed yet"
948)]
949pub struct FwdDepError {
950    // This error is used for many `Handle` types, but there's no point in making this generic, so
951    // we just flatten them all to `Handle<()>` here.
952    subject: Handle<()>,
953    subject_kind: &'static str,
954    depends_on: Handle<()>,
955    depends_on_kind: &'static str,
956}
957
958impl<T> Handle<T> {
959    /// Check that `self` is valid within `arena` using [`Arena::check_contains_handle`].
960    pub(self) fn check_valid_for(self, arena: &Arena<T>) -> Result<(), InvalidHandleError> {
961        arena.check_contains_handle(self)?;
962        Ok(())
963    }
964
965    /// Check that `self` is valid within `arena` using [`UniqueArena::check_contains_handle`].
966    pub(self) fn check_valid_for_uniq(
967        self,
968        arena: &UniqueArena<T>,
969    ) -> Result<(), InvalidHandleError>
970    where
971        T: Eq + Hash,
972    {
973        arena.check_contains_handle(self)?;
974        Ok(())
975    }
976
977    /// Check that `depends_on` was constructed before `self` by comparing handle indices.
978    ///
979    /// If `self` is a valid handle (i.e., it has been validated using [`Self::check_valid_for`])
980    /// and this function returns [`Ok`], then it may be assumed that `depends_on` is also valid.
981    /// In [`naga`](crate)'s current arena-based implementation, this is useful for validating
982    /// recursive definitions of arena-based values in linear time.
983    ///
984    /// # Errors
985    ///
986    /// If `depends_on`'s handle is from the same [`Arena`] as `self'`s, but not constructed earlier
987    /// than `self`'s, this function returns an error.
988    pub(self) fn check_dep(self, depends_on: Self) -> Result<Self, FwdDepError> {
989        if depends_on < self {
990            Ok(self)
991        } else {
992            let erase_handle_type = |handle: Handle<_>| {
993                Handle::new(NonMaxU32::new((handle.index()).try_into().unwrap()).unwrap())
994            };
995            Err(FwdDepError {
996                subject: erase_handle_type(self),
997                subject_kind: core::any::type_name::<T>(),
998                depends_on: erase_handle_type(depends_on),
999                depends_on_kind: core::any::type_name::<T>(),
1000            })
1001        }
1002    }
1003
1004    /// Like [`Self::check_dep`], except for [`Option`]al handle values.
1005    pub(self) fn check_dep_opt(self, depends_on: Option<Self>) -> Result<Self, FwdDepError> {
1006        self.check_dep_iter(depends_on.into_iter())
1007    }
1008
1009    /// Like [`Self::check_dep`], except for [`Iterator`]s over handle values.
1010    pub(self) fn check_dep_iter(
1011        self,
1012        depends_on: impl Iterator<Item = Self>,
1013    ) -> Result<Self, FwdDepError> {
1014        for handle in depends_on {
1015            self.check_dep(handle)?;
1016        }
1017        Ok(self)
1018    }
1019}
1020
1021impl<T> crate::arena::Range<T> {
1022    pub(self) fn check_valid_for(&self, arena: &Arena<T>) -> Result<(), BadRangeError> {
1023        arena.check_contains_range(self)
1024    }
1025}
1026
1027#[test]
1028fn constant_deps() {
1029    use crate::{Constant, Expression, Literal, Span, Type, TypeInner};
1030
1031    let nowhere = Span::default();
1032
1033    let mut types = UniqueArena::new();
1034    let mut const_exprs = Arena::new();
1035    let mut fun_exprs = Arena::new();
1036    let mut constants = Arena::new();
1037    let overrides = Arena::new();
1038
1039    let i32_handle = types.insert(
1040        Type {
1041            name: None,
1042            inner: TypeInner::Scalar(crate::Scalar::I32),
1043        },
1044        nowhere,
1045    );
1046
1047    // Construct a self-referential constant by misusing a handle to
1048    // fun_exprs as a constant initializer.
1049    let fun_expr = fun_exprs.append(Expression::Literal(Literal::I32(42)), nowhere);
1050    let self_referential_const = constants.append(
1051        Constant {
1052            name: None,
1053            ty: i32_handle,
1054            init: fun_expr,
1055        },
1056        nowhere,
1057    );
1058    let _self_referential_expr =
1059        const_exprs.append(Expression::Constant(self_referential_const), nowhere);
1060
1061    for handle_and_expr in const_exprs.iter() {
1062        assert!(super::Validator::validate_const_expression_handles(
1063            handle_and_expr,
1064            &constants,
1065            &overrides,
1066        )
1067        .is_err());
1068    }
1069}
1070
1071#[test]
1072fn array_size_deps() {
1073    use super::Validator;
1074    use crate::{ArraySize, Expression, Override, Scalar, Span, Type, TypeInner};
1075
1076    let nowhere = Span::default();
1077
1078    let mut m = crate::Module::default();
1079
1080    let ty_u32 = m.types.insert(
1081        Type {
1082            name: Some("u32".to_string()),
1083            inner: TypeInner::Scalar(Scalar::U32),
1084        },
1085        nowhere,
1086    );
1087    let ex_zero = m
1088        .global_expressions
1089        .append(Expression::ZeroValue(ty_u32), nowhere);
1090    let ty_handle = m.overrides.append(
1091        Override {
1092            name: None,
1093            id: None,
1094            ty: ty_u32,
1095            init: Some(ex_zero),
1096        },
1097        nowhere,
1098    );
1099    let ty_arr = m.types.insert(
1100        Type {
1101            name: Some("bad_array".to_string()),
1102            inner: TypeInner::Array {
1103                base: ty_u32,
1104                size: ArraySize::Pending(ty_handle),
1105                stride: 4,
1106            },
1107        },
1108        nowhere,
1109    );
1110
1111    // Everything should be okay now.
1112    assert!(Validator::validate_module_handles(&m).is_ok());
1113
1114    // Mutate `ex_zero`'s type to `ty_arr`, introducing a cycle.
1115    // Validation should catch the cycle.
1116    m.global_expressions[ex_zero] = Expression::ZeroValue(ty_arr);
1117    assert!(Validator::validate_module_handles(&m).is_err());
1118}
1119
1120#[test]
1121fn array_size_override() {
1122    use super::Validator;
1123    use crate::{ArraySize, Override, Scalar, Span, Type, TypeInner};
1124
1125    let nowhere = Span::default();
1126
1127    let mut m = crate::Module::default();
1128
1129    let ty_u32 = m.types.insert(
1130        Type {
1131            name: Some("u32".to_string()),
1132            inner: TypeInner::Scalar(Scalar::U32),
1133        },
1134        nowhere,
1135    );
1136
1137    let bad_override: Handle<Override> = Handle::new(NonMaxU32::new(1000).unwrap());
1138    let _ty_arr = m.types.insert(
1139        Type {
1140            name: Some("bad_array".to_string()),
1141            inner: TypeInner::Array {
1142                base: ty_u32,
1143                size: ArraySize::Pending(bad_override),
1144                stride: 4,
1145            },
1146        },
1147        nowhere,
1148    );
1149
1150    assert!(Validator::validate_module_handles(&m).is_err());
1151}
1152
1153#[test]
1154fn override_init_deps() {
1155    use super::Validator;
1156    use crate::{ArraySize, Expression, Override, Scalar, Span, Type, TypeInner};
1157
1158    let nowhere = Span::default();
1159
1160    let mut m = crate::Module::default();
1161
1162    let ty_u32 = m.types.insert(
1163        Type {
1164            name: Some("u32".to_string()),
1165            inner: TypeInner::Scalar(Scalar::U32),
1166        },
1167        nowhere,
1168    );
1169    let ex_zero = m
1170        .global_expressions
1171        .append(Expression::ZeroValue(ty_u32), nowhere);
1172    let r#override = m.overrides.append(
1173        Override {
1174            name: Some("bad_override".into()),
1175            id: None,
1176            ty: ty_u32,
1177            init: Some(ex_zero),
1178        },
1179        nowhere,
1180    );
1181    let ty_arr = m.types.insert(
1182        Type {
1183            name: Some("bad_array".to_string()),
1184            inner: TypeInner::Array {
1185                base: ty_u32,
1186                size: ArraySize::Pending(r#override),
1187                stride: 4,
1188            },
1189        },
1190        nowhere,
1191    );
1192    let ex_arr = m
1193        .global_expressions
1194        .append(Expression::ZeroValue(ty_arr), nowhere);
1195
1196    assert!(Validator::validate_module_handles(&m).is_ok());
1197
1198    // Mutate `r#override`'s initializer to `ex_arr`, introducing a cycle.
1199    // Validation should catch the cycle.
1200    m.overrides[r#override].init = Some(ex_arr);
1201    assert!(Validator::validate_module_handles(&m).is_err());
1202}