implsuper::Validator { /// Validates that all handles within `module` are: /// /// * Valid, in the sense that they contain indices within each arena structure inside the /// [`crate::Module`] type. /// * No arena contents contain any items that have forward dependencies; that is, the value /// associated with a handle only may contain references to handles in the same arena that /// were constructed before it. /// /// By validating the above conditions, we free up subsequent logic to assume that handle /// accesses are infallible. /// /// # Errors /// /// Errors returned by this method are intentionally sparse, for simplicity of implementation. /// It is expected that only buggy frontends or fuzzers should ever emit IR that fails this /// validation pass. pub(super) fn validate_module_handles(module: &crate::Module) -> Result<(), ValidationError> { let &crate::Module { ref constants, ref overrides, ref entry_points, ref functions, ref global_variables, ref types, ref special_types, ref global_expressions, ref diagnostic_filters, ref diagnostic_filter_leaf,
} = module;
// Because types can refer to global expressions and vice versa, to // ensure the overall structure is free of cycles, we must traverse them // both in tandem. // // Try to visit all types and global expressions in an order such that // each item refers only to previously visited items. If we succeed, // that shows that there cannot be any cycles, since walking any edge // advances you towards the beginning of the visiting order. // // Validate all the handles in types and expressions as we traverse the // arenas. letmut global_exprs_iter = global_expressions.iter().peekable(); for (th, t) in types.iter() { // Imagine the `for` loop and `global_exprs_iter` as two fingers // walking the type and global expression arenas. They don't visit // elements at the same rate: sometimes one processes a bunch of // elements while the other one stays still. But at each point, they // check that the two ranges of elements they've visited only refer // to other elements in those ranges. // // For brevity, we'll say 'handles behind `global_exprs_iter`' to // mean handles that have already been produced by // `global_exprs_iter`. Once `global_exprs_iter` returns `None`, all // global expression handles are 'behind' it. // // At this point: // // - All types visited by prior iterations (that is, before // `th`/`t`) refer only to expressions behind `global_exprs_iter`. // // On the first iteration, this is obviously true: there are no // prior iterations, and `global_exprs_iter` hasn't produced // anything yet. At the bottom of the loop, we'll claim that it's // true for `th`/`t` as well, so the condition remains true when // we advance to the next type. // // - All expressions behind `global_exprs_iter` refer only to // previously visited types. // // Again, trivially true at the start, and we'll show it's true // about each expression that `global_exprs_iter` produces. // // Once we also check that arena elements only refer to prior // elements in that arena, we can see that `th`/`t` does not // participate in a cycle: it only refers to previously visited // types and expressions behind `global_exprs_iter`, and none of // those refer to `th`/`t`, because they passed the same checks // before we reached `th`/`t`. iflet Some(max_expr) = Self::validate_type_handles((th, t), overrides)? {
max_expr.check_valid_for(global_expressions)?; // Since `t` refers to `max_expr`, if we want our invariants to // remain true, we must advance `global_exprs_iter` beyond // `max_expr`. whilelet Some((eh, e)) = global_exprs_iter.next_if(|&(eh, _)| eh <= max_expr) { iflet Some(max_type) = Self::validate_const_expression_handles((eh, e), constants, overrides)?
{ // Show that `eh` refers only to previously visited types.
th.check_dep(max_type)?;
} // We've advanced `global_exprs_iter` past `eh` already. But // since we now know that `eh` refers only to previously // visited types, it is again true that all expressions // behind `global_exprs_iter` refer only to previously // visited types. So we can continue to the next expression.
}
}
// Here we know that if `th` refers to any expressions at all, // `max_expr` is the latest one. And we know that `max_expr` is // behind `global_exprs_iter`. So `th` refers only to expressions // behind `global_exprs_iter`, and the invariants will still be // true on the next iteration.
}
// Since we also enforced the usual intra-arena rules that expressions // refer only to prior expressions, expressions can only form cycles if // they include types. But we've shown that all types are acyclic, so // all expressions must be acyclic as well. // // Validate the remaining expressions normally. for handle_and_expr in global_exprs_iter { Self::validate_const_expression_handles(handle_and_expr, constants, overrides)?;
}
let validate_type = |handle| Self::validate_type_handle(handle, types); let validate_const_expr =
|handle| Self::validate_expression_handle(handle, global_expressions);
for (_handle, constant) in constants.iter() { let &crate::Constant { name: _, ty, init } = constant;
validate_type(ty)?;
validate_const_expr(init)?;
}
for (_handle, override_) in overrides.iter() { let &crate::Override {
name: _,
id: _,
ty,
init,
} = override_;
validate_type(ty)?; iflet Some(init_expr) = init {
validate_const_expr(init_expr)?;
}
}
for (_handle, global_variable) in global_variables.iter() { let &crate::GlobalVariable {
name: _,
space: _,
binding: _,
ty,
init,
} = global_variable;
validate_type(ty)?; iflet Some(init_expr) = init {
validate_const_expr(init_expr)?;
}
}
/// Validate all handles that occur in `ty`, whose handle is `handle`. /// /// If `ty` refers to any expressions, return the highest-indexed expression /// handle that it uses. This is used for detecting cycles between the /// expression and type arenas. fn validate_type_handles(
(handle, ty): (Handle<crate::Type>, &crate::Type),
overrides: &Arena<crate::Override>,
) -> Result<Option<Handle<crate::Expression>>, InvalidHandleError> { let max_expr = match ty.inner { crate::TypeInner::Scalar { .. }
| crate::TypeInner::Vector { .. }
| crate::TypeInner::Matrix { .. }
| crate::TypeInner::ValuePointer { .. }
| crate::TypeInner::Atomic { .. }
| crate::TypeInner::Image { .. }
| crate::TypeInner::Sampler { .. }
| crate::TypeInner::AccelerationStructure
| crate::TypeInner::RayQuery => None, crate::TypeInner::Pointer { base, space: _ } => {
handle.check_dep(base)?;
None
} crate::TypeInner::Array { base, size, .. }
| crate::TypeInner::BindingArray { base, size, .. } => {
handle.check_dep(base)?; match size { crate::ArraySize::Pending(pending) => match pending { crate::PendingArraySize::Expression(expr) => Some(expr), crate::PendingArraySize::Override(h) => { Self::validate_override_handle(h, overrides)?; let override_ = &overrides[h];
handle.check_dep(override_.ty)?;
override_.init
}
}, crate::ArraySize::Constant(_) | crate::ArraySize::Dynamic => None,
}
} crate::TypeInner::Struct { ref members,
span: _,
} => {
handle.check_dep_iter(members.iter().map(|m| m.ty))?;
None
}
};
Ok(max_expr)
}
/// Validate all handles that occur in `expression`, whose handle is `handle`. /// /// If `expression` refers to any `Type`s, return the highest-indexed type /// handle that it uses. This is used for detecting cycles between the /// expression and type arenas. fn validate_const_expression_handles(
(handle, expression): (Handle<crate::Expression>, &crate::Expression),
constants: &Arena<crate::Constant>,
overrides: &Arena<crate::Override>,
) -> Result<Option<Handle<crate::Type>>, InvalidHandleError> { let validate_constant = |handle| Self::validate_constant_handle(handle, constants); let validate_override = |handle| Self::validate_override_handle(handle, overrides);
#[derive(Clone, Debug, thiserror::Error)] #[cfg_attr(test, derive(PartialEq))] #[error( "{subject:?} of kind {subject_kind:?} depends on {depends_on:?} of kind {depends_on_kind}, \
which has not been processed yet"
)] pubstruct FwdDepError { // This error is used for many `Handle` types, but there's no point in making this generic, so // we just flatten them all to `Handle<()>` here.
subject: Handle<()>,
subject_kind: &'static str,
depends_on: Handle<()>,
depends_on_kind: &'static str,
}
impl<T> Handle<T> { /// Check that `self` is valid within `arena` using [`Arena::check_contains_handle`]. pub(self) fn check_valid_for(self, arena: &Arena<T>) -> Result<(), InvalidHandleError> {
arena.check_contains_handle(self)?;
Ok(())
}
/// Check that `self` is valid within `arena` using [`UniqueArena::check_contains_handle`]. pub(self) fn check_valid_for_uniq( self,
arena: &UniqueArena<T>,
) -> Result<(), InvalidHandleError> where
T: Eq + Hash,
{
arena.check_contains_handle(self)?;
Ok(())
}
/// Check that `depends_on` was constructed before `self` by comparing handle indices. /// /// If `self` is a valid handle (i.e., it has been validated using [`Self::check_valid_for`]) /// and this function returns [`Ok`], then it may be assumed that `depends_on` is also valid. /// In [`naga`](crate)'s current arena-based implementation, this is useful for validating /// recursive definitions of arena-based values in linear time. /// /// # Errors /// /// If `depends_on`'s handle is from the same [`Arena`] as `self'`s, but not constructed earlier /// than `self`'s, this function returns an error. pub(self) fn check_dep(self, depends_on: Self) -> Result<Self, FwdDepError> { if depends_on < self {
Ok(self)
} else { let erase_handle_type = |handle: Handle<_>| {
Handle::new(NonMaxU32::new((handle.index()).try_into().unwrap()).unwrap())
};
Err(FwdDepError {
subject: erase_handle_type(self),
subject_kind: std::any::type_name::<T>(),
depends_on: erase_handle_type(depends_on),
depends_on_kind: std::any::type_name::<T>(),
})
}
}
/// Like [`Self::check_dep`], except for [`Option`]al handle values. pub(self) fn check_dep_opt(self, depends_on: Option<Self>) -> Result<Self, FwdDepError> { self.check_dep_iter(depends_on.into_iter())
}
/// Like [`Self::check_dep`], except for [`Iterator`]s over handle values. pub(self) fn check_dep_iter( self,
depends_on: impl Iterator<Item = Self>,
) -> Result<Self, FwdDepError> { for handle in depends_on { self.check_dep(handle)?;
}
Ok(self)
}
}
let i32_handle = types.insert( Type {
name: None,
inner: TypeInner::Scalar(crate::Scalar::I32),
},
nowhere,
);
// Construct a self-referential constant by misusing a handle to // fun_exprs as a constant initializer. let fun_expr = fun_exprs.append(Expression::Literal(Literal::I32(42)), nowhere); let self_referential_const = constants.append(
Constant {
name: None,
ty: i32_handle,
init: fun_expr,
},
nowhere,
); let _self_referential_expr =
const_exprs.append(Expression::Constant(self_referential_const), nowhere);
for handle_and_expr in const_exprs.iter() {
assert!(super::Validator::validate_const_expression_handles(
handle_and_expr,
&constants,
&overrides,
)
.is_err());
}
}
let ty_u32 = m.types.insert( Type {
name: Some("u32".to_string()),
inner: TypeInner::Scalar(Scalar::U32),
},
nowhere,
); let ex_zero = m
.global_expressions
.append(Expression::ZeroValue(ty_u32), nowhere); let ty_arr = m.types.insert( Type {
name: Some("bad_array".to_string()),
inner: TypeInner::Array {
base: ty_u32,
size: ArraySize::Pending(PendingArraySize::Expression(ex_zero)),
stride: 4,
},
},
nowhere,
);
// Everything should be okay now.
assert!(Validator::validate_module_handles(&m).is_ok());
// Mutate `ex_zero`'s type to `ty_arr`, introducing a cycle. // Validation should catch the cycle.
m.global_expressions[ex_zero] = Expression::ZeroValue(ty_arr);
assert!(Validator::validate_module_handles(&m).is_err());
}
// Mutate `r#override`'s initializer to `ex_arr`, introducing a cycle. // Validation should catch the cycle.
m.overrides[r#override].init = Some(ex_arr);
assert!(Validator::validate_module_handles(&m).is_err());
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.30 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.