#[derive(Clone, Debug, thiserror::Error)] #[cfg_attr(test, derive(PartialEq))] pubenum ExpressionError { #[error("Used by a statement before it was introduced into the scope by any of the dominating blocks")]
NotInScope, #[error("Base type {0:?} is not compatible with this expression")]
InvalidBaseType(Handle<crate::Expression>), #[error("Accessing with index {0:?} can't be done")]
InvalidIndexType(Handle<crate::Expression>), #[error("Accessing {0:?} via a negative index is invalid")]
NegativeIndex(Handle<crate::Expression>), #[error("Accessing index {1} is out of {0:?} bounds")]
IndexOutOfBounds(Handle<crate::Expression>, u32), #[error("Function argument {0:?} doesn't exist")]
FunctionArgumentDoesntExist(u32), #[error("Loading of {0:?} can't be done")]
InvalidPointerType(Handle<crate::Expression>), #[error("Array length of {0:?} can't be done")]
InvalidArrayType(Handle<crate::Expression>), #[error("Get intersection of {0:?} can't be done")]
InvalidRayQueryType(Handle<crate::Expression>), #[error("Splatting {0:?} can't be done")]
InvalidSplatType(Handle<crate::Expression>), #[error("Swizzling {0:?} can't be done")]
InvalidVectorType(Handle<crate::Expression>), #[error("Swizzle component {0:?} is outside of vector size {1:?}")]
InvalidSwizzleComponent(crate::SwizzleComponent, crate::VectorSize), #[error(transparent)]
Compose(#[from] super::ComposeError), #[error("Cannot construct zero value of {0:?} because it is not a constructible type")]
InvalidZeroValue(Handle<crate::Type>), #[error(transparent)]
IndexableLength(#[from] IndexableLengthError), #[error("Operation {0:?} can't work with {1:?}")]
InvalidUnaryOperandType(crate::UnaryOperator, Handle<crate::Expression>), #[error( "Operation {:?} can't work with {:?} (of type {:?}) and {:?} (of type {:?})",
op,
lhs_expr,
lhs_type,
rhs_expr,
rhs_type
)]
InvalidBinaryOperandTypes {
op: crate::BinaryOperator,
lhs_expr: Handle<crate::Expression>,
lhs_type: crate::TypeInner,
rhs_expr: Handle<crate::Expression>,
rhs_type: crate::TypeInner,
}, #[error("Expected selection argument types to match, but reject value of type {reject:?} does not match accept value of value {accept:?}")]
SelectValuesTypeMismatch {
accept: crate::TypeInner,
reject: crate::TypeInner,
}, #[error("Expected selection condition to be a boolean value, got {actual:?}")]
SelectConditionNotABool { actual: crate::TypeInner }, #[error("Relational argument {0:?} is not a boolean vector")]
InvalidBooleanVector(Handle<crate::Expression>), #[error("Relational argument {0:?} is not a float")]
InvalidFloatArgument(Handle<crate::Expression>), #[error("Type resolution failed")] Type(#[from] ResolveError), #[error("Not a global variable")]
ExpectedGlobalVariable, #[error("Not a global variable or a function argument")]
ExpectedGlobalOrArgument, #[error("Needs to be an binding array instead of {0:?}")]
ExpectedBindingArrayType(Handle<crate::Type>), #[error("Needs to be an image instead of {0:?}")]
ExpectedImageType(Handle<crate::Type>), #[error("Needs to be an image instead of {0:?}")]
ExpectedSamplerType(Handle<crate::Type>), #[error("Unable to operate on image class {0:?}")]
InvalidImageClass(crate::ImageClass), #[error("Image atomics are not supported for storage format {0:?}")]
InvalidImageFormat(crate::StorageFormat), #[error("Image atomics require atomic storage access, {0:?} is insufficient")]
InvalidImageStorageAccess(crate::StorageAccess), #[error("Derivatives can only be taken from scalar and vector floats")]
InvalidDerivative, #[error("Image array index parameter is misplaced")]
InvalidImageArrayIndex, #[error("Cannot textureLoad from a specific multisample sample on a non-multisampled image.")]
InvalidImageSampleSelector, #[error("Cannot textureLoad from a multisampled image without specifying a sample.")]
MissingImageSampleSelector, #[error("Cannot textureLoad with a specific mip level on a non-mipmapped image.")]
InvalidImageLevelSelector, #[error("Cannot textureLoad from a mipmapped image without specifying a level.")]
MissingImageLevelSelector, #[error("Image array index type of {0:?} is not an integer scalar")]
InvalidImageArrayIndexType(Handle<crate::Expression>), #[error("Image sample or level-of-detail index's type of {0:?} is not an integer scalar")]
InvalidImageOtherIndexType(Handle<crate::Expression>), #[error("Image coordinate type of {1:?} does not match dimension {0:?}")]
InvalidImageCoordinateType(crate::ImageDimension, Handle<crate::Expression>), #[error("Comparison sampling mismatch: image has class {image:?}, but the sampler is comparison={sampler}, and the reference was provided={has_ref}")]
ComparisonSamplingMismatch {
image: crate::ImageClass,
sampler: bool,
has_ref: bool,
}, #[error("Sample offset must be a const-expression")]
InvalidSampleOffsetExprType, #[error("Sample offset constant {1:?} doesn't match the image dimension {0:?}")]
InvalidSampleOffset(crate::ImageDimension, Handle<crate::Expression>), #[error("Depth reference {0:?} is not a scalar float")]
InvalidDepthReference(Handle<crate::Expression>), #[error("Depth sample level can only be Auto or Zero")]
InvalidDepthSampleLevel, #[error("Gather level can only be Zero")]
InvalidGatherLevel, #[error("Gather component {0:?} doesn't exist in the image")]
InvalidGatherComponent(crate::SwizzleComponent), #[error("Gather can't be done for image dimension {0:?}")]
InvalidGatherDimension(crate::ImageDimension), #[error("Sample level (exact) type {0:?} has an invalid type")]
InvalidSampleLevelExactType(Handle<crate::Expression>), #[error("Sample level (bias) type {0:?} is not a scalar float")]
InvalidSampleLevelBiasType(Handle<crate::Expression>), #[error("Bias can't be done for image dimension {0:?}")]
InvalidSampleLevelBiasDimension(crate::ImageDimension), #[error("Sample level (gradient) of {1:?} doesn't match the image dimension {0:?}")]
InvalidSampleLevelGradientType(crate::ImageDimension, Handle<crate::Expression>), #[error("Clamping sample coordinate to edge is not supported with {0}")]
InvalidSampleClampCoordinateToEdge(alloc::string::String), #[error("Unable to cast")]
InvalidCastArgument, #[error("Invalid argument count for {0:?}")]
WrongArgumentCount(crate::MathFunction), #[error("Argument [{1}] to {0:?} as expression {2:?} has an invalid type.")]
InvalidArgumentType(crate::MathFunction, u32, Handle<crate::Expression>), #[error( "workgroupUniformLoad result type can't be {0:?}. It can only be a constructible type."
)]
InvalidWorkGroupUniformLoadResultType(Handle<crate::Type>), #[error("Shader requires capability {0:?}")]
MissingCapabilities(super::Capabilities), #[error(transparent)]
Literal(#[from] LiteralError), #[error("{0:?} is not supported for Width {2} {1:?} arguments yet, see https://github.com/gfx-rs/wgpu/issues/5276")]
UnsupportedWidth(crate::MathFunction, crate::ScalarKind, crate::Bytes), #[error("Invalid operand for cooperative op")]
InvalidCooperativeOperand(Handle<crate::Expression>), #[error("Shift amount exceeds the bit width of {lhs_type:?}")]
ShiftAmountTooLarge {
lhs_type: crate::TypeInner,
rhs_expr: Handle<crate::Expression>,
}, #[error("Division by zero")]
DivideByZero,
}
#[derive(Clone, Debug, thiserror::Error)] #[cfg_attr(test, derive(PartialEq))] pubenum ConstExpressionError { #[error("The expression is not a constant or override expression")]
NonConstOrOverride, #[error("The expression is not a fully evaluated constant expression")]
NonFullyEvaluatedConst, #[error(transparent)]
Compose(#[from] super::ComposeError), #[error("Splatting {0:?} can't be done")]
InvalidSplatType(Handle<crate::Expression>), #[error("Type resolution failed")] Type(#[from] ResolveError), #[error(transparent)]
Literal(#[from] LiteralError), #[error(transparent)]
Width(#[from] super::r#type::WidthError),
}
impl core::ops::Index<Handle<crate::Expression>> for ExpressionTypeResolver<'_> { type Output = crate::TypeInner;
#[allow(clippy::panic)] fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output { if handle < self.root { self.info[handle].ty.inner_with(self.types)
} else { // `Validator::validate_module_handles` should have caught this.
panic!( "Depends on {:?}, which has not been processed yet", self.root
)
}
}
}
/// Return an error if a constant shift amount in `right` exceeds the bit /// width of `left_ty`. /// /// This function promises to return an error in cases where (1) the /// expression is well-typed, (2) `left_ty` is a concrete integer, and /// (3) the shift will overflow. It does not return an error in cases where /// the expression is not well-typed (e.g. vector dimension mismatch), /// because those will be rejected elsewhere. fn validate_constant_shift_amounts(
left_ty: &crate::TypeInner,
right: Handle<crate::Expression>,
module: &crate::Module,
function: &crate::Function,
) -> Result<(), ExpressionError> { fn is_overflowing_shift(
left_ty: &crate::TypeInner,
right: Handle<crate::Expression>,
module: &crate::Module,
function: &crate::Function,
) -> bool { let Some((vec_size, scalar)) = left_ty.vector_size_and_scalar() else { returnfalse;
}; if !matches!(
scalar.kind, crate::ScalarKind::Sint | crate::ScalarKind::Uint
) { returnfalse;
} let lhs_bits = u32::from(8 * scalar.width); if vec_size.is_none() { let shift_amount = module
.to_ctx()
.get_const_val_from::<u32, _>(right, &function.expressions);
shift_amount.ok().is_some_and(|s| s >= lhs_bits)
} else { match function.expressions[right] { crate::Expression::ZeroValue(_) => false, // zero shift does not overflow crate::Expression::Splat { value, .. } => module
.to_ctx()
.get_const_val_from::<u32, _>(value, &function.expressions)
.ok()
.is_some_and(|s| s >= lhs_bits), crate::Expression::Compose {
ty: _, ref components,
} => components.iter().any(|comp| {
module
.to_ctx()
.get_const_val_from::<u32, _>(*comp, &function.expressions)
.ok()
.is_some_and(|s| s >= lhs_bits)
}),
_ => false,
}
}
}
/// Return an error if a constant divisor in `right` evaluates to zero for /// an integer division or remainder operation. /// /// This function promises to return an error in cases where (1) the /// expression is well-typed, (2) `left_ty` is a concrete integer or a /// vector, and (3) `right` is a const-expression that evaluates to zero. /// It does not return an error in cases where the expression is not /// well-typed (e.g. vector dimension mismatch), because those will be /// rejected elsewhere. fn validate_constant_divisor(
left_ty: &crate::TypeInner,
right: Handle<crate::Expression>,
module: &crate::Module,
function: &crate::Function,
) -> Result<(), ExpressionError> { fn contains_zero(
handle: Handle<crate::Expression>,
expressions: &crate::Arena<crate::Expression>,
module: &crate::Module,
) -> bool { match expressions[handle] { crate::Expression::Literal(_) | crate::Expression::ZeroValue(_) => module
.to_ctx()
.get_const_val_from::<u32, _>(handle, expressions)
.ok()
.is_some_and(|v| v == 0), crate::Expression::Splat { value, .. } => contains_zero(value, expressions, module), crate::Expression::Compose { ref components, .. } => components
.iter()
.any(|&comp| contains_zero(comp, expressions, module)), crate::Expression::Constant(c) => {
contains_zero(module.constants[c].init, &module.global_expressions, module)
}
_ => false,
}
}
#[allow(clippy::too_many_arguments)] pub(super) fn validate_expression(
&self,
root: Handle<crate::Expression>,
expression: &crate::Expression,
function: &crate::Function,
module: &crate::Module,
info: &FunctionInfo,
mod_info: &ModuleInfo,
expr_kind: &crate::proc::ExpressionKindTracker,
) -> Result<ShaderStages, ExpressionError> { usecrate::{Expression as E, Scalar as Sc, ScalarKind as Sk, TypeInner as Ti};
let resolver = ExpressionTypeResolver {
root,
types: &module.types,
info,
};
let stages = match *expression {
E::Access { base, index } => { let base_type = &resolver[base]; match *base_type {
Ti::Matrix { .. }
| Ti::Vector { .. }
| Ti::Array { .. }
| Ti::Pointer { .. }
| Ti::ValuePointer { size: Some(_), .. }
| Ti::BindingArray { .. } => {} ref other => {
log::debug!("Indexing of {other:?}"); return Err(ExpressionError::InvalidBaseType(base));
}
}; match resolver[index] { //TODO: only allow one of these
Ti::Scalar(Sc {
kind: Sk::Sint | Sk::Uint,
..
}) => {} ref other => {
log::debug!("Indexing by {other:?}"); return Err(ExpressionError::InvalidIndexType(index));
}
}
// If index is const we can do check for non-negative index match module
.to_ctx()
.get_const_val_from(index, &function.expressions)
{
Ok(value) => { let length = ifself.overrides_resolved {
base_type.indexable_length_resolved(module)
} else {
base_type.indexable_length_pending(module)
}?; // If we know both the length and the index, we can do the // bounds check now. ifletcrate::proc::IndexableLength::Known(known_length) = length { if value >= known_length { return Err(ExpressionError::IndexOutOfBounds(base, value));
}
}
}
Err(crate::proc::ConstValueError::Negative) => { return Err(ExpressionError::NegativeIndex(base))
}
Err(crate::proc::ConstValueError::NonConst) => {}
Err(crate::proc::ConstValueError::InvalidType) => { return Err(ExpressionError::InvalidIndexType(index))
}
}
iflet Some(component) = gather { match dim { crate::ImageDimension::D2 | crate::ImageDimension::Cube => {} crate::ImageDimension::D1 | crate::ImageDimension::D3 => { return Err(ExpressionError::InvalidGatherDimension(dim))
}
}; let max_component = match class { crate::ImageClass::Depth { .. } => crate::SwizzleComponent::X,
_ => crate::SwizzleComponent::W,
}; if component > max_component { return Err(ExpressionError::InvalidGatherComponent(component));
} match level { crate::SampleLevel::Zero => {}
_ => return Err(ExpressionError::InvalidGatherLevel),
}
}
// Clamping coordinate to edge is only supported with 2d non-arrayed, sampled images // when sampling from level Zero without any offset, gather, or depth comparison. if clamp_to_edge { if !matches!(
class, crate::ImageClass::Sampled {
kind: crate::ScalarKind::Float,
multi: false
} | crate::ImageClass::External
) { return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
alloc::format!("image class `{class:?}`"),
));
} if dim != crate::ImageDimension::D2 { return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
alloc::format!("image dimension `{dim:?}`"),
));
} if gather.is_some() { return Err(ExpressionError::InvalidSampleClampCoordinateToEdge( "gather".into(),
));
} if array_index.is_some() { return Err(ExpressionError::InvalidSampleClampCoordinateToEdge( "array index".into(),
));
} if offset.is_some() { return Err(ExpressionError::InvalidSampleClampCoordinateToEdge( "offset".into(),
));
} if level != crate::SampleLevel::Zero { return Err(ExpressionError::InvalidSampleClampCoordinateToEdge( "non-zero level".into(),
));
} if depth_ref.is_some() { return Err(ExpressionError::InvalidSampleClampCoordinateToEdge( "depth comparison".into(),
));
}
}
// External textures can only be sampled using clamp_to_edge. if matches!(class, crate::ImageClass::External) && !clamp_to_edge { return Err(ExpressionError::InvalidImageClass(class));
}
// Start with the set of all overloads available for `fun`. letmut overloads = fun.overloads();
log::debug!( "initial overloads for {:?}: {:#?}",
fun,
overloads.for_debug(&module.types)
);
// If any argument is not a constant expression, then no // overloads that accept abstract values should be considered. // `OverloadSet::concrete_only` is supposed to help impose this // restriction. However, no `MathFunction` accepts a mix of // abstract and concrete arguments, so we don't need to worry // about that here.
for (i, (&expr, &ty)) in actuals.iter().zip(actual_types).enumerate() { // Remove overloads that cannot accept an `i`'th // argument arguments of type `ty`.
overloads = overloads.arg(i, ty, &module.types);
log::debug!( "overloads after arg {i}: {:#?}",
overloads.for_debug(&module.types)
);
if overloads.is_empty() {
log::debug!("all overloads eliminated"); return Err(ExpressionError::InvalidArgumentType(fun, i as u32, expr));
}
}
if actuals.len() < overloads.min_arguments() { return Err(ExpressionError::WrongArgumentCount(fun));
}
ShaderStages::all()
}
E::As {
expr,
kind,
convert,
} => { letmut base_scalar = match resolver[expr] { crate::TypeInner::Scalar(scalar) | crate::TypeInner::Vector { scalar, .. } => {
scalar
} crate::TypeInner::Matrix { scalar, .. } => scalar,
_ => return Err(ExpressionError::InvalidCastArgument),
};
base_scalar.kind = kind; iflet Some(width) = convert {
base_scalar.width = width;
} ifself.check_width(base_scalar).is_err() { return Err(ExpressionError::InvalidCastArgument);
}
ShaderStages::all()
}
E::CallResult(function) => mod_info.functions[function.index()].available_stages,
E::AtomicResult { .. } => { // These expressions are validated when we check the `Atomic` statement // that refers to them, because we have all the information we need at // that point. The checks driven by `Validator::needs_visit` ensure // that this expression is indeed visited by one `Atomic` statement.
ShaderStages::all()
}
E::WorkGroupUniformLoadResult { ty } => { ifself.types[ty.index()]
.flags // Sized | Constructible is exactly the types currently supported by // WorkGroupUniformLoad
.contains(TypeFlags::SIZED | TypeFlags::CONSTRUCTIBLE)
{
ShaderStages::COMPUTE_LIKE
} else { return Err(ExpressionError::InvalidWorkGroupUniformLoadResultType(ty));
}
}
E::ArrayLength(expr) => match resolver[expr] {
Ti::Pointer { base, .. } => { let base_ty = &resolver.types[base]; iflet Ti::Array {
size: crate::ArraySize::Dynamic,
..
} = base_ty.inner
{
ShaderStages::all()
} else { return Err(ExpressionError::InvalidArrayType(expr));
}
} ref other => {
log::debug!("Array length of {other:?}"); return Err(ExpressionError::InvalidArrayType(expr));
}
},
E::RayQueryProceedResult => ShaderStages::all(),
E::RayQueryGetIntersection {
query,
committed: _,
} => match resolver[query] {
Ti::Pointer {
base,
space: crate::AddressSpace::Function,
} => match resolver.types[base].inner {
Ti::RayQuery { .. } => ShaderStages::all(), ref other => {
log::debug!("Intersection result of a pointer to {other:?}"); return Err(ExpressionError::InvalidRayQueryType(query));
}
}, ref other => {
log::debug!("Intersection result of {other:?}"); return Err(ExpressionError::InvalidRayQueryType(query));
}
},
E::RayQueryVertexPositions {
query,
committed: _,
} => match resolver[query] {
Ti::Pointer {
base,
space: crate::AddressSpace::Function,
} => match resolver.types[base].inner {
Ti::RayQuery {
vertex_return: true,
} => ShaderStages::all(), ref other => {
log::debug!("Intersection result of a pointer to {other:?}"); return Err(ExpressionError::InvalidRayQueryType(query));
}
}, ref other => {
log::debug!("Intersection result of {other:?}"); return Err(ExpressionError::InvalidRayQueryType(query));
}
},
E::SubgroupBallotResult | E::SubgroupOperationResult { .. } => self.subgroup_stages,
E::CooperativeLoad { ref data, .. } => { if resolver[data.pointer]
.pointer_base_type()
.and_then(|tr| tr.inner_with(&module.types).scalar())
.is_none()
{ return Err(ExpressionError::InvalidPointerType(data.pointer));
}
ShaderStages::COMPUTE
}
E::CooperativeMultiplyAdd { a, b, c } => { let roles = [ crate::CooperativeRole::A, crate::CooperativeRole::B, crate::CooperativeRole::C,
]; for (operand, expected_role) in [a, b, c].into_iter().zip(roles) { match resolver[operand] {
Ti::CooperativeMatrix { role, .. } if role == expected_role => {} ref other => {
log::debug!("{expected_role:?} operand type: {other:?}"); return Err(ExpressionError::InvalidCooperativeOperand(a));
}
}
}
ShaderStages::COMPUTE
}
};
Ok(stages)
}
/// Using F64 in a function's expression arena is forbidden. #[test] fn f64_runtime_literals() { let result = validate_with_expression( crate::Expression::Literal(crate::Literal::F64(0.57721_56649)), super::Capabilities::default(),
); let error = result.unwrap_err().into_inner();
assert!(matches!(
error, crate::valid::ValidationError::Function {
source: super::FunctionError::Expression {
source: ExpressionError::Literal(LiteralError::Width( super::r#type::WidthError::MissingCapability {
name: "f64",
flag: "FLOAT64",
}
),),
..
},
..
}
));
let result = validate_with_expression( crate::Expression::Literal(crate::Literal::F64(0.57721_56649)), super::Capabilities::default() | super::Capabilities::FLOAT64,
);
assert!(result.is_ok());
}
/// Using F64 in a module's constant expression arena is forbidden. #[test] fn f64_const_literals() { let result = validate_with_const_expression( crate::Expression::Literal(crate::Literal::F64(0.57721_56649)), super::Capabilities::default(),
); let error = result.unwrap_err().into_inner();
assert!(matches!(
error, crate::valid::ValidationError::ConstExpression {
source: ConstExpressionError::Literal(LiteralError::Width( super::r#type::WidthError::MissingCapability {
name: "f64",
flag: "FLOAT64",
}
)),
..
}
));
let result = validate_with_const_expression( crate::Expression::Literal(crate::Literal::F64(0.57721_56649)), super::Capabilities::default() | super::Capabilities::FLOAT64,
);
assert!(result.is_ok());
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.35Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-26)
¤
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.