From 6551ab3a270db908ebbc9b11862b2e79a3d96cf7 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 9 Sep 2022 16:24:13 -0700 Subject: [PATCH] Document arithmetic binary operation type rules. --- src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/valid/expression.rs | 9 +++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5db2a397df..c4893b73a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -876,6 +876,45 @@ pub enum UnaryOperator { } /// Operation that can be applied on two values. +/// +/// ## Arithmetic type rules +/// +/// The arithmetic operations `Add`, `Subtract`, `Multiply`, `Divide`, and +/// `Modulo` can all be applied to [`Scalar`] types other than [`Bool`], or +/// [`Vector`]s thereof. Both operands must have the same type. +/// +/// `Add` and `Subtract` can also be applied to [`Matrix`] values. Both operands +/// must have the same type. +/// +/// `Multiply` supports additional cases: +/// +/// - A [`Matrix`] or [`Vector`] can be multiplied by a scalar [`Float`], +/// either on the left or the right. +/// +/// - A [`Matrix`] on the left can be multiplied by a [`Vector`] on the right +/// if the matrix has as many columns as the vector has components (`matCxR +/// * VecC`). +/// +/// - A [`Vector`] on the left can be multiplied by a [`Matrix`] on the right +/// if the matrix has as many rows as the vector has components (`VecR * +/// matCxR`). +/// +/// - Two matrices can be multiplied if the left operand has as many columns +/// as the right operand has rows (`matNxR * matCxN`). +/// +/// In all the above `Multiply` cases, the byte widths of the underlying scalar +/// types of both operands must be the same. +/// +/// Note that `Multiply` supports mixed vector and scalar operations directly, +/// whereas the other arithmetic operations require an explicit [`Splat`] for +/// mixed-type use. +/// +/// [`Scalar`]: TypeInner::Scalar +/// [`Vector`]: TypeInner::Vector +/// [`Matrix`]: TypeInner::Matrix +/// [`Float`]: ScalarKind::Float +/// [`Bool`]: ScalarKind::Bool +/// [`Splat`]: Expression::Splat #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "deserialize", derive(Deserialize))] diff --git a/src/valid/expression.rs b/src/valid/expression.rs index 6279ed3622..0f173707f3 100644 --- a/src/valid/expression.rs +++ b/src/valid/expression.rs @@ -740,16 +740,18 @@ impl super::Validator { _ => false, }, Bo::Multiply => { - let kind_match = match left_inner.scalar_kind() { + let kind_allowed = match left_inner.scalar_kind() { Some(Sk::Uint | Sk::Sint | Sk::Float) => true, Some(Sk::Bool) | None => false, }; let types_match = match (left_inner, right_inner) { + // Straight scalar and mixed scalar/vector. (&Ti::Scalar { kind: kind1, .. }, &Ti::Scalar { kind: kind2, .. }) | (&Ti::Vector { kind: kind1, .. }, &Ti::Scalar { kind: kind2, .. }) | (&Ti::Scalar { kind: kind1, .. }, &Ti::Vector { kind: kind2, .. }) => { kind1 == kind2 } + // Scalar/matrix. ( &Ti::Scalar { kind: Sk::Float, .. @@ -762,6 +764,7 @@ impl super::Validator { kind: Sk::Float, .. }, ) => true, + // Vector/vector. ( &Ti::Vector { kind: kind1, @@ -774,6 +777,7 @@ impl super::Validator { .. }, ) => kind1 == kind2 && size1 == size2, + // Matrix * vector. ( &Ti::Matrix { columns, .. }, &Ti::Vector { @@ -782,6 +786,7 @@ impl super::Validator { .. }, ) => columns == size, + // Vector * matrix. ( &Ti::Vector { kind: Sk::Float, @@ -807,7 +812,7 @@ impl super::Validator { | Ti::Matrix { width, .. } => width, _ => 0, }; - kind_match && types_match && left_width == right_width + kind_allowed && types_match && left_width == right_width } Bo::Equal | Bo::NotEqual => left_inner.is_sized() && left_inner == right_inner, Bo::Less | Bo::LessEqual | Bo::Greater | Bo::GreaterEqual => {