Core: Centralize shape checks and elide proven runtime assertions

libeigen/eigen!3354

diff --git a/Eigen/Core b/Eigen/Core
index 4677123..2330b76 100644
--- a/Eigen/Core
+++ b/Eigen/Core
@@ -193,6 +193,7 @@
 #include "src/Core/util/ForwardDeclarations.h"
 #include "src/Core/util/StaticAssert.h"
 #include "src/Core/util/XprHelper.h"
+#include "src/Core/util/ShapeChecks.h"
 #include "src/Core/util/Memory.h"
 #include "src/Core/util/IntegralConstant.h"
 #include "src/Core/util/Serializer.h"
diff --git a/Eigen/src/Cholesky/BunchKaufman.h b/Eigen/src/Cholesky/BunchKaufman.h
index dd451f4..d35554a 100644
--- a/Eigen/src/Cholesky/BunchKaufman.h
+++ b/Eigen/src/Cholesky/BunchKaufman.h
@@ -932,7 +932,7 @@
 template <typename MatrixType, int UpLo_>
 template <typename InputType>
 BunchKaufman<MatrixType, UpLo_>& BunchKaufman<MatrixType, UpLo_>::compute(const EigenBase<InputType>& a) {
-  eigen_assert(a.rows() == a.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(a);
   m_matrix = a.derived();
   return computeInPlace();
 }
@@ -940,7 +940,7 @@
 /** \internal Factorizes the matrix held in m_matrix, which is overwritten by the packed factors. */
 template <typename MatrixType, int UpLo_>
 BunchKaufman<MatrixType, UpLo_>& BunchKaufman<MatrixType, UpLo_>::computeInPlace() {
-  eigen_assert(m_matrix.rows() == m_matrix.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(m_matrix);
   const Index size = m_matrix.rows();
 
   // L1 norm of the implicit self-adjoint matrix, for rcond().
@@ -995,7 +995,7 @@
 template <typename Derived>
 bool BunchKaufman<MatrixType, UpLo_>::solveInPlace(MatrixBase<Derived>& bAndX) const {
   eigen_assert(m_isInitialized && "BunchKaufman is not initialized.");
-  eigen_assert(m_matrix.rows() == bAndX.rows());
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(m_matrix, bAndX);
   bAndX = this->solve(bAndX);
   return true;
 }
diff --git a/Eigen/src/Cholesky/LDLT.h b/Eigen/src/Cholesky/LDLT.h
index 5ecbda8..deca15e 100644
--- a/Eigen/src/Cholesky/LDLT.h
+++ b/Eigen/src/Cholesky/LDLT.h
@@ -357,7 +357,7 @@
     using Scalar = typename MatrixType::Scalar;
     using RealScalar = typename MatrixType::RealScalar;
     using IndexType = typename TranspositionType::StorageIndex;
-    eigen_assert(mat.rows() == mat.cols());
+    EIGEN_INTERNAL_ASSERT_SQUARE(mat);
     const Index size = mat.rows();
     bool found_zero_pivot = false;
     bool ret = true;
@@ -471,7 +471,8 @@
     using RealScalar = typename MatrixType::RealScalar;
 
     const Index size = mat.rows();
-    eigen_assert(mat.cols() == size && w.size() == size);
+    EIGEN_INTERNAL_ASSERT_SQUARE(mat);
+    EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(w, mat);
 
     RealScalar alpha = 1;
 
@@ -554,7 +555,7 @@
 template <typename MatrixType, int UpLo_>
 template <typename InputType>
 LDLT<MatrixType, UpLo_>& LDLT<MatrixType, UpLo_>::compute(const EigenBase<InputType>& a) {
-  eigen_assert(a.rows() == a.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(a);
   const Index size = a.rows();
 
   m_matrix = a.derived();
@@ -600,7 +601,7 @@
   using IndexType = typename TranspositionType::StorageIndex;
   const Index size = w.rows();
   if (m_isInitialized) {
-    eigen_assert(m_matrix.rows() == size);
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(m_matrix, w);
   } else {
     m_matrix.resize(size, size);
     m_matrix.setZero();
@@ -711,7 +712,7 @@
 template <typename Derived>
 bool LDLT<MatrixType, UpLo_>::solveInPlace(MatrixBase<Derived>& bAndX) const {
   eigen_assert(m_isInitialized && "LDLT is not initialized.");
-  eigen_assert(m_matrix.rows() == bAndX.rows());
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(m_matrix, bAndX);
 
   bAndX = this->solve(bAndX);
 
diff --git a/Eigen/src/Cholesky/LLT.h b/Eigen/src/Cholesky/LLT.h
index e4ffac8..716f9d5 100644
--- a/Eigen/src/Cholesky/LLT.h
+++ b/Eigen/src/Cholesky/LLT.h
@@ -331,7 +331,8 @@
   using TempVecSegment = typename TempVectorType::SegmentReturnType;
 
   Index n = mat.cols();
-  eigen_assert(mat.rows() == n && vec.size() == n);
+  EIGEN_INTERNAL_ASSERT_SQUARE(mat);
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS(vec, mat);
 
   TempVectorType temp;
 
@@ -388,7 +389,7 @@
   static Index unblocked(MatrixType& mat) {
     using std::sqrt;
 
-    eigen_assert(mat.rows() == mat.cols());
+    EIGEN_INTERNAL_ASSERT_SQUARE(mat);
     const Index size = mat.rows();
     for (Index k = 0; k < size; ++k) {
       Index rs = size - k - 1;  // remaining size
@@ -409,7 +410,7 @@
 
   template <typename MatrixType>
   static Index blocked(MatrixType& m) {
-    eigen_assert(m.rows() == m.cols());
+    EIGEN_INTERNAL_ASSERT_SQUARE(m);
     Index size = m.rows();
     // The unblocked kernel walks row k of the factor, which strides through memory: it wins up to about 48 columns,
     // and beyond that 16-column blocks keep those walks short while amortizing the level-3 updates.
@@ -501,7 +502,7 @@
 template <typename MatrixType, int UpLo_>
 template <typename InputType>
 LLT<MatrixType, UpLo_>& LLT<MatrixType, UpLo_>::compute(const EigenBase<InputType>& a) {
-  eigen_assert(a.rows() == a.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(a);
   const Index size = a.rows();
   m_matrix.resize(size, size);
   if (!internal::is_same_dense(m_matrix, a.derived())) m_matrix = a.derived();
@@ -525,7 +526,7 @@
 template <typename VectorType>
 LLT<MatrixType_, UpLo_>& LLT<MatrixType_, UpLo_>::rankUpdate(const VectorType& v, const RealScalar& sigma) {
   EIGEN_STATIC_ASSERT_VECTOR_ONLY(VectorType);
-  eigen_assert(v.size() == m_matrix.cols());
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS(v, m_matrix);
   eigen_assert(m_isInitialized);
   if (internal::llt_inplace<typename MatrixType::Scalar, UpLo>::rankUpdate(m_matrix, v, sigma) >= 0)
     m_info = NumericalIssue;
@@ -597,7 +598,7 @@
 template <typename Derived>
 void LLT<MatrixType, UpLo_>::solveInPlace(const MatrixBase<Derived>& bAndX) const {
   eigen_assert(m_isInitialized && "LLT is not initialized.");
-  eigen_assert(m_matrix.rows() == bAndX.rows());
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(m_matrix, bAndX);
   matrixL().solveInPlace(bAndX);
   matrixU().solveInPlace(bAndX);
 }
diff --git a/Eigen/src/Core/Assign.h b/Eigen/src/Core/Assign.h
index baec68b..e4dbd0c 100644
--- a/Eigen/src/Core/Assign.h
+++ b/Eigen/src/Core/Assign.h
@@ -25,12 +25,11 @@
   enum { SameType = std::is_same<typename Derived::Scalar, typename OtherDerived::Scalar>::value };
 
   EIGEN_STATIC_ASSERT_LVALUE(Derived)
-  EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Derived, OtherDerived)
   EIGEN_STATIC_ASSERT(
       SameType,
       YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
 
-  eigen_assert(rows() == other.rows() && cols() == other.cols());
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE(*this, other);
   internal::call_assignment_no_alias(derived(), other.derived());
 
   return derived();
diff --git a/Eigen/src/Core/AssignEvaluator.h b/Eigen/src/Core/AssignEvaluator.h
index ae3a1e9..419f090 100644
--- a/Eigen/src/Core/AssignEvaluator.h
+++ b/Eigen/src/Core/AssignEvaluator.h
@@ -907,9 +907,7 @@
 template <typename DstXprType, typename SrcXprType, typename Functor>
 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE constexpr void resize_if_allowed(DstXprType& dst, const SrcXprType& src,
                                                                        const Functor& /*func*/) {
-  EIGEN_ONLY_USED_FOR_DEBUG(dst);
-  EIGEN_ONLY_USED_FOR_DEBUG(src);
-  eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE(dst, src);
 }
 
 template <typename DstXprType, typename SrcXprType, typename T1, typename T2>
@@ -926,7 +924,8 @@
     }
 #else
     dst.resize(dstRows, dstCols);
-    eigen_assert(dst.rows() == dstRows && dst.cols() == dstCols);
+    EIGEN_INTERNAL_CHECK_SHAPE(dst.rows() == dstRows && dst.cols() == dstCols, YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES,
+                               internal::matching_shapes<DstXprType, SrcXprType>);
 #endif
   }
 }
@@ -1140,7 +1139,7 @@
     Index dstCols = src.cols();
     if ((dst.rows() != dstRows) || (dst.cols() != dstCols)) dst.resize(dstRows, dstCols);
 
-    eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(dst, src);
     src.evalTo(dst);
   }
 
@@ -1154,7 +1153,7 @@
     Index dstCols = src.cols();
     if ((dst.rows() != dstRows) || (dst.cols() != dstCols)) dst.resize(dstRows, dstCols);
 
-    eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(dst, src);
     src.addTo(dst);
   }
 
@@ -1166,7 +1165,7 @@
     Index dstCols = src.cols();
     if ((dst.rows() != dstRows) || (dst.cols() != dstCols)) dst.resize(dstRows, dstCols);
 
-    eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(dst, src);
     src.subTo(dst);
   }
 };
diff --git a/Eigen/src/Core/CommaInitializer.h b/Eigen/src/Core/CommaInitializer.h
index fe67f78..139bd7c 100644
--- a/Eigen/src/Core/CommaInitializer.h
+++ b/Eigen/src/Core/CommaInitializer.h
@@ -34,7 +34,7 @@
 
   EIGEN_DEVICE_FUNC constexpr CommaInitializer(XprType& xpr, const Scalar& s)
       : m_xpr(xpr), m_row(0), m_col(1), m_currentBlockRows(1) {
-    eigen_assert(m_xpr.rows() > 0 && m_xpr.cols() > 0 && "Cannot comma-initialize a 0x0 matrix (operator<<)");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_xpr, "Cannot comma-initialize a 0x0 matrix (operator<<)");
     m_xpr.coeffRef(0, 0) = s;
   }
 
diff --git a/Eigen/src/Core/ConcatOp.h b/Eigen/src/Core/ConcatOp.h
index 6ac7732..5f1bbbd 100644
--- a/Eigen/src/Core/ConcatOp.h
+++ b/Eigen/src/Core/ConcatOp.h
@@ -107,19 +107,14 @@
         (std::is_same<typename LhsType::Scalar, typename RhsType::Scalar>::value),
         YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
     EIGEN_STATIC_ASSERT_SAME_XPR_KIND(LhsType, RhsType)
-    EIGEN_STATIC_ASSERT(Direction != Horizontal || int(LhsType::RowsAtCompileTime) == Dynamic ||
-                            int(RhsType::RowsAtCompileTime) == Dynamic ||
-                            int(LhsType::RowsAtCompileTime) == int(RhsType::RowsAtCompileTime),
-                        YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
-    EIGEN_STATIC_ASSERT(Direction != Vertical || int(LhsType::ColsAtCompileTime) == Dynamic ||
-                            int(RhsType::ColsAtCompileTime) == Dynamic ||
-                            int(LhsType::ColsAtCompileTime) == int(RhsType::ColsAtCompileTime),
-                        YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
-    EIGEN_IF_CONSTEXPR (Direction == Vertical) {
-      eigen_assert(lhs.cols() == rhs.cols() && "vcat: number of columns must match");
-    } else {
-      eigen_assert(lhs.rows() == rhs.rows() && "hcat: number of rows must match");
-    }
+    // Select the relation first: C++14 would instantiate static assertions in both branches of an ordinary if.
+    // TODO(C++17): use if constexpr with EIGEN_INTERNAL_ASSERT_SAME_COLS/EIGEN_INTERNAL_ASSERT_SAME_ROWS directly.
+    EIGEN_INTERNAL_CHECK_SHAPE(
+        (Direction == Vertical ? lhs.cols() == rhs.cols() : lhs.rows() == rhs.rows()) &&
+            (Direction == Vertical ? "vcat: number of columns must match" : "hcat: number of rows must match"),
+        YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES,
+        std::conditional_t<Direction == Vertical, internal::matching_cols<LhsType, RhsType>,
+                           internal::matching_rows<LhsType, RhsType>>);
   }
 
   EIGEN_DEVICE_FUNC constexpr Index rows() const {
diff --git a/Eigen/src/Core/ConditionEstimator.h b/Eigen/src/Core/ConditionEstimator.h
index 41e2d87..f0649c0 100644
--- a/Eigen/src/Core/ConditionEstimator.h
+++ b/Eigen/src/Core/ConditionEstimator.h
@@ -64,7 +64,7 @@
   using RealVector = typename internal::plain_col_type<MatrixType, RealScalar>::type;
   const bool is_complex = (NumTraits<Scalar>::IsComplex != 0);
 
-  eigen_assert(dec.rows() == dec.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(dec);
   const Index n = dec.rows();
   if (n == 0) return RealScalar(0);
 
@@ -149,7 +149,7 @@
 typename Decomposition::RealScalar rcond_estimate_helper(typename Decomposition::RealScalar matrix_norm,
                                                          const Decomposition& dec) {
   using RealScalar = typename Decomposition::RealScalar;
-  eigen_assert(dec.rows() == dec.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(dec);
   if (dec.rows() == 0) return NumTraits<RealScalar>::infinity();
   if (numext::is_exactly_zero(matrix_norm)) return RealScalar(0);
   if (dec.rows() == 1) return RealScalar(1);
diff --git a/Eigen/src/Core/CwiseBinaryOp.h b/Eigen/src/Core/CwiseBinaryOp.h
index 82625d0..8064d27 100644
--- a/Eigen/src/Core/CwiseBinaryOp.h
+++ b/Eigen/src/Core/CwiseBinaryOp.h
@@ -100,7 +100,7 @@
   EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE CwiseBinaryOp(const Lhs& aLhs, const Rhs& aRhs,
                                                                 const BinaryOp& func = BinaryOp())
       : m_lhs(aLhs), m_rhs(aRhs), m_functor(func) {
-    eigen_assert(aLhs.rows() == aRhs.rows() && aLhs.cols() == aRhs.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(aLhs, aRhs);
   }
 
   EIGEN_DEVICE_FUNC constexpr Index rows() const noexcept {
diff --git a/Eigen/src/Core/CwiseTernaryOp.h b/Eigen/src/Core/CwiseTernaryOp.h
index 8672c63..27444b1 100644
--- a/Eigen/src/Core/CwiseTernaryOp.h
+++ b/Eigen/src/Core/CwiseTernaryOp.h
@@ -116,7 +116,8 @@
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CwiseTernaryOp(const Arg1& a1, const Arg2& a2, const Arg3& a3,
                                                        const TernaryOp& func = TernaryOp())
       : m_arg1(a1), m_arg2(a2), m_arg3(a3), m_functor(func) {
-    eigen_assert(a1.rows() == a2.rows() && a1.cols() == a2.cols() && a1.rows() == a3.rows() && a1.cols() == a3.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(a1, a2);
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(a1, a3);
   }
 
   EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Index rows() const {
diff --git a/Eigen/src/Core/DenseBase.h b/Eigen/src/Core/DenseBase.h
index 9cdb42b..de5a3b2 100644
--- a/Eigen/src/Core/DenseBase.h
+++ b/Eigen/src/Core/DenseBase.h
@@ -395,7 +395,7 @@
   template <typename OtherDerived>
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void swap(const DenseBase<OtherDerived>& other) {
     EIGEN_STATIC_ASSERT(!OtherDerived::IsPlainObjectBase, THIS_EXPRESSION_IS_NOT_A_LVALUE__IT_IS_READ_ONLY);
-    eigen_assert(rows() == other.rows() && cols() == other.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(*this, other);
     call_assignment(derived(), other.const_cast_derived(), internal::swap_assign_op<Scalar>());
   }
 
@@ -404,7 +404,7 @@
    */
   template <typename OtherDerived>
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void swap(PlainObjectBase<OtherDerived>& other) {
-    eigen_assert(rows() == other.rows() && cols() == other.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(*this, other);
     call_assignment(derived(), other.derived(), internal::swap_assign_op<Scalar>());
   }
 
@@ -448,7 +448,7 @@
 
   /** \returns the unique coefficient of a 1x1 expression */
   EIGEN_DEVICE_FUNC CoeffReturnType value() const {
-    EIGEN_STATIC_ASSERT_SIZE_1x1(Derived) eigen_assert(this->rows() == 1 && this->cols() == 1);
+    EIGEN_INTERNAL_ASSERT_SCALAR(*this);
     return derived().coeff(0, 0);
   }
 
diff --git a/Eigen/src/Core/FindCoeff.h b/Eigen/src/Core/FindCoeff.h
index d7f87b6..86ba513 100644
--- a/Eigen/src/Core/FindCoeff.h
+++ b/Eigen/src/Core/FindCoeff.h
@@ -353,7 +353,7 @@
 template <typename Derived, typename IndexType, typename Func>
 EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar findCoeff(const DenseBase<Derived>& mat, Func& func,
                                                                        IndexType* rowPtr, IndexType* colPtr) {
-  eigen_assert(mat.rows() > 0 && mat.cols() > 0 && "you are using an empty matrix");
+  EIGEN_INTERNAL_ASSERT_NONEMPTY(mat);
   using Scalar = typename DenseBase<Derived>::Scalar;
   using FindCoeffImpl = internal::find_coeff_impl<Derived, Func>;
   Index outer = 0;
@@ -368,7 +368,7 @@
 template <typename Derived, typename IndexType, typename Func>
 EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar findCoeff(const DenseBase<Derived>& mat, Func& func,
                                                                        IndexType* indexPtr) {
-  eigen_assert(mat.size() > 0 && "you are using an empty matrix");
+  EIGEN_INTERNAL_ASSERT_NONEMPTY(mat);
   EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
   using Scalar = typename DenseBase<Derived>::Scalar;
   using FindCoeffImpl = internal::find_coeff_impl<Derived, Func>;
diff --git a/Eigen/src/Core/InnerProduct.h b/Eigen/src/Core/InnerProduct.h
index 6957564..d334298 100644
--- a/Eigen/src/Core/InnerProduct.h
+++ b/Eigen/src/Core/InnerProduct.h
@@ -23,14 +23,9 @@
 struct inner_product_assert {
   EIGEN_STATIC_ASSERT_VECTOR_ONLY(Lhs)
   EIGEN_STATIC_ASSERT_VECTOR_ONLY(Rhs)
-  EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(Lhs, Rhs)
-#ifndef EIGEN_NO_DEBUG
   static EIGEN_DEVICE_FUNC void run(const Lhs& lhs, const Rhs& rhs) {
-    eigen_assert((lhs.size() == rhs.size()) && "Inner product: lhs and rhs vectors must have same size");
+    EIGEN_INTERNAL_ASSERT_SAME_SIZE_MSG(lhs, rhs, "Inner product: lhs and rhs vectors must have same size");
   }
-#else
-  static EIGEN_DEVICE_FUNC void run(const Lhs&, const Rhs&) {}
-#endif
 };
 
 template <typename Func, typename Lhs, typename Rhs>
diff --git a/Eigen/src/Core/MatrixNorms.h b/Eigen/src/Core/MatrixNorms.h
index 16c206b..a81143f 100644
--- a/Eigen/src/Core/MatrixNorms.h
+++ b/Eigen/src/Core/MatrixNorms.h
@@ -369,7 +369,7 @@
 template <unsigned int UpLo, typename Derived>
 EIGEN_DEVICE_FUNC typename Derived::RealScalar selfadjoint_l1_norm(const MatrixBase<Derived>& matrix) {
   static_assert(UpLo == Upper || UpLo == Lower, "UpLo must be Upper or Lower");
-  eigen_assert(matrix.rows() == matrix.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matrix);
   return selfadjoint_l1norm<Derived, UpLo>(matrix.derived()).run();
 }
 
diff --git a/Eigen/src/Core/PermutationMatrix.h b/Eigen/src/Core/PermutationMatrix.h
index cb073fd..860d8fc 100644
--- a/Eigen/src/Core/PermutationMatrix.h
+++ b/Eigen/src/Core/PermutationMatrix.h
@@ -249,7 +249,7 @@
   }
   template <typename Lhs, typename Rhs>
   void assignProduct(const Lhs& lhs, const Rhs& rhs) {
-    eigen_assert(lhs.cols() == rhs.rows());
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(lhs, rhs);
     for (Index i = 0; i < rows(); ++i) indices().coeffRef(i) = lhs.indices().coeff(rhs.indices().coeff(i));
   }
 #endif
@@ -271,7 +271,7 @@
   template <typename Other>
   inline PlainPermutationType operator*(const InverseImpl<Other, PermutationStorage>& other) const {
     const auto& rhs = other.derived().nestedExpression();
-    eigen_assert(size() == rhs.size());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(*this, rhs);
     PlainPermutationType result(size());
     // (P * Q.inverse())(Q(i)) = P(i).
     for (Index i = 0; i < size(); ++i) result.indices().coeffRef(rhs.indices().coeff(i)) = indices().coeff(i);
diff --git a/Eigen/src/Core/PlainObjectBase.h b/Eigen/src/Core/PlainObjectBase.h
index 430640d..bebc44f 100644
--- a/Eigen/src/Core/PlainObjectBase.h
+++ b/Eigen/src/Core/PlainObjectBase.h
@@ -356,20 +356,22 @@
    * Note that copying a row-vector into a vector (and conversely) is allowed.
    * The resizing, if any, is then done in the appropriate way so that row-vectors
    * remain row-vectors and vectors remain vectors.
+   * Resizing a vector from a statically non-vector expression is rejected at
+   * compile time. With EIGEN_NO_STATIC_ASSERT, or for dynamic source dimensions,
+   * this precondition is checked by a debug assertion instead.
    */
   template <typename OtherDerived>
   EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE void resizeLike(const EigenBase<OtherDerived>& _other) {
     const OtherDerived& other = _other.derived();
+    EIGEN_INTERNAL_ASSERT_VECTOR_IF(IsVectorAtCompileTime, other);
 #ifndef EIGEN_NO_DEBUG
     internal::check_rows_cols_for_overflow<MaxSizeAtCompileTime, MaxRowsAtCompileTime, MaxColsAtCompileTime>::run(
         other.rows(), other.cols());
 #endif
     const Index othersize = other.rows() * other.cols();
     EIGEN_IF_CONSTEXPR (RowsAtCompileTime == 1) {
-      eigen_assert(other.rows() == 1 || other.cols() == 1);
       resize(1, othersize);
     } else EIGEN_IF_CONSTEXPR (ColsAtCompileTime == 1) {
-      eigen_assert(other.rows() == 1 || other.cols() == 1);
       resize(othersize, 1);
     } else {
       resize(other.rows(), other.cols());
@@ -724,10 +726,15 @@
   template <typename OtherDerived>
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void _resize_to_match(const EigenBase<OtherDerived>& other) {
 #ifdef EIGEN_NO_AUTOMATIC_RESIZING
-    eigen_assert((this->size() == 0 ||
-                  (IsVectorAtCompileTime ? ((other.rows() == 1 || other.cols() == 1) && this->size() == other.size())
-                                         : (rows() == other.rows() && cols() == other.cols()))) &&
-                 "Size mismatch. Automatic resizing is disabled because EIGEN_NO_AUTOMATIC_RESIZING is defined");
+    constexpr bool shape_is_proven = IsVectorAtCompileTime ? (internal::vector_dimensions<OtherDerived>::Proven &&
+                                                              internal::matching_sizes<Derived, OtherDerived>::Proven)
+                                                           : internal::matching_shapes<Derived, OtherDerived>::Proven;
+    EIGEN_INTERNAL_SHAPE_ASSERT(
+        shape_is_proven,
+        (this->size() == 0 ||
+         (IsVectorAtCompileTime ? ((other.rows() == 1 || other.cols() == 1) && this->size() == other.size())
+                                : (rows() == other.rows() && cols() == other.cols()))) &&
+            "Size mismatch. Automatic resizing is disabled because EIGEN_NO_AUTOMATIC_RESIZING is defined");
     if (this->size() == 0) resizeLike(other);
     EIGEN_ONLY_USED_FOR_DEBUG(other);
 #else
diff --git a/Eigen/src/Core/Product.h b/Eigen/src/Core/Product.h
index 8c56014..68ca72c 100644
--- a/Eigen/src/Core/Product.h
+++ b/Eigen/src/Core/Product.h
@@ -221,8 +221,7 @@
   using AdjointReturnType = typename internal::product_transpose_helper<Lhs, Rhs, Option>::AdjointType;
 
   EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Product(const Lhs& lhs, const Rhs& rhs) : m_lhs(lhs), m_rhs(rhs) {
-    eigen_assert(lhs.cols() == rhs.rows() && "invalid matrix product" &&
-                 "if you wanted a coeff-wise or a dot product use the respective explicit functions");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(lhs, rhs);
   }
 
   EIGEN_DEVICE_FUNC constexpr Index rows() const noexcept { return m_lhs.rows(); }
@@ -289,15 +288,19 @@
 
  public:
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar coeff(Index row, Index col) const {
-    EIGEN_STATIC_ASSERT(EnableCoeff, THIS_METHOD_IS_ONLY_FOR_INNER_OR_LAZY_PRODUCTS);
-    eigen_assert((Option == LazyProduct) || (this->rows() == 1 && this->cols() == 1));
+    EIGEN_INTERNAL_CHECK_SHAPE((Option == LazyProduct) || (this->rows() == 1 && this->cols() == 1),
+                               THIS_METHOD_IS_ONLY_FOR_INNER_OR_LAZY_PRODUCTS,
+                               std::conditional_t<Option == LazyProduct, internal::dimension_relation<1, 1>,
+                                                  internal::fixed_dimensions<Derived, 1, 1>>);
 
     return internal::evaluator<Derived>(derived()).coeff(row, col);
   }
 
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar coeff(Index i) const {
-    EIGEN_STATIC_ASSERT(EnableCoeff, THIS_METHOD_IS_ONLY_FOR_INNER_OR_LAZY_PRODUCTS);
-    eigen_assert((Option == LazyProduct) || (this->rows() == 1 && this->cols() == 1));
+    EIGEN_INTERNAL_CHECK_SHAPE((Option == LazyProduct) || (this->rows() == 1 && this->cols() == 1),
+                               THIS_METHOD_IS_ONLY_FOR_INNER_OR_LAZY_PRODUCTS,
+                               std::conditional_t<Option == LazyProduct, internal::dimension_relation<1, 1>,
+                                                  internal::fixed_dimensions<Derived, 1, 1>>);
 
     return internal::evaluator<Derived>(derived()).coeff(i);
   }
diff --git a/Eigen/src/Core/ProductEvaluators.h b/Eigen/src/Core/ProductEvaluators.h
index f0a97cb..bd898a2 100644
--- a/Eigen/src/Core/ProductEvaluators.h
+++ b/Eigen/src/Core/ProductEvaluators.h
@@ -179,7 +179,7 @@
   using SrcXprType = Product<Lhs, Rhs, Options>;
   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(DstXprType& dst, const SrcXprType& src,
                                                         const internal::add_assign_op<Scalar, Scalar>&) {
-    eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(dst, src);
     // FIXME shall we handle nested_eval here?
     generic_product_impl<Lhs, Rhs>::addTo(dst, src.lhs(), src.rhs());
   }
@@ -192,7 +192,7 @@
   using SrcXprType = Product<Lhs, Rhs, Options>;
   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(DstXprType& dst, const SrcXprType& src,
                                                         const internal::sub_assign_op<Scalar, Scalar>&) {
-    eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(dst, src);
     // FIXME shall we handle nested_eval here?
     generic_product_impl<Lhs, Rhs>::subTo(dst, src.lhs(), src.rhs());
   }
@@ -1119,9 +1119,7 @@
   template <typename Dest, typename Alpha>
   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(Dest& dst, const MatrixType& matrix,
                                                         const DiagonalType& diagonal, const Alpha& alpha) {
-    eigen_assert(((ProductOrder == OnTheLeft && diagonal.size() == matrix.rows()) ||
-                  (ProductOrder == OnTheRight && diagonal.size() == matrix.cols())) &&
-                 "invalid matrix product");
+    EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS_MSG(diagonal, matrix, ProductOrder == OnTheLeft, "invalid matrix product");
 
     const Index rows = matrix.rows();
     const Index cols = matrix.cols();
@@ -1259,8 +1257,8 @@
   template <bool Accumulate, typename Dest, typename Alpha>
   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void runImpl(Dest& dst, const MatrixType& matrix,
                                                             const DiagonalType& diagonal, const Alpha& alpha) {
-    eigen_assert(matrix.rows() == matrix.cols() && "SelfAdjointView is only for squared matrices");
-    eigen_assert(diagonal.size() == matrix.rows() && "invalid matrix product");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(matrix, "SelfAdjointView is only for squared matrices");
+    EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS_MSG(diagonal, matrix, "invalid matrix product");
 
     const Index size = matrix.rows();
 
diff --git a/Eigen/src/Core/Redux.h b/Eigen/src/Core/Redux.h
index b07e049..524dd92 100644
--- a/Eigen/src/Core/Redux.h
+++ b/Eigen/src/Core/Redux.h
@@ -276,7 +276,7 @@
 
   template <typename XprType>
   EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Scalar run(const Evaluator& eval, const Func& func, const XprType& xpr) {
-    eigen_assert(xpr.rows() > 0 && xpr.cols() > 0 && "you are using an empty matrix");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY(xpr);
     const Index innerSize = xpr.innerSize();
     const Index outerSize = xpr.outerSize();
     using Bounds = redux_max_size<XprType>;
@@ -354,7 +354,7 @@
   template <typename XprType>
   EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Scalar run(const Evaluator& eval, const Func& func, const XprType& xpr) {
     const Index size = xpr.size();
-    eigen_assert(size > 0 && "you are using an empty matrix");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_SIZE(xpr, size);
     // Do not generate wide reduction bodies for bounded expressions that cannot
     // reach their cutoff (GCC can otherwise diagnose their unreachable loads).
     EIGEN_IF_CONSTEXPR (functor_is_commutative<Func>::value) {
@@ -538,7 +538,7 @@
 
   template <typename XprType>
   EIGEN_DEVICE_FUNC static Scalar run(const Evaluator& eval, const Func& func, const XprType& xpr) {
-    eigen_assert(xpr.rows() > 0 && xpr.cols() > 0 && "you are using an empty matrix");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY(xpr);
     const Index innerSize = xpr.innerSize();
     const Index outerSize = xpr.outerSize();
     const Index packetedInnerSize = numext::round_down(innerSize, PacketSize);
@@ -639,7 +639,7 @@
   template <typename XprType>
   EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE Scalar run(const Evaluator& eval, const Func& func, const XprType& xpr) {
     EIGEN_ONLY_USED_FOR_DEBUG(xpr);
-    eigen_assert(xpr.rows() > 0 && xpr.cols() > 0 && "you are using an empty matrix");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY(xpr);
     if (VectorizedSize > 0) {
       Scalar res = func.predux(
           redux_vec_linear_unroller<Func, Evaluator, 0, Size / PacketSize>::template run<PacketType>(eval, func));
@@ -792,7 +792,7 @@
 template <typename Func>
 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE typename internal::traits<Derived>::Scalar DenseBase<Derived>::redux(
     const Func& func) const {
-  eigen_assert(this->rows() > 0 && this->cols() > 0 && "you are using an empty matrix");
+  EIGEN_INTERNAL_ASSERT_NONEMPTY(*this);
 
   using ThisEvaluator = typename internal::redux_evaluator<Derived>;
   ThisEvaluator thisEval(derived());
diff --git a/Eigen/src/Core/Ref.h b/Eigen/src/Core/Ref.h
index 3e725ad..023c5a8 100644
--- a/Eigen/src/Core/Ref.h
+++ b/Eigen/src/Core/Ref.h
@@ -118,12 +118,11 @@
     // Determine runtime rows and columns.
     Index rows = expr.rows();
     Index cols = expr.cols();
+    EIGEN_INTERNAL_ASSERT_VECTOR_IF(PlainObjectType::IsVectorAtCompileTime, expr);
     EIGEN_IF_CONSTEXPR (PlainObjectType::RowsAtCompileTime == 1) {
-      eigen_assert(expr.rows() == 1 || expr.cols() == 1);
       rows = 1;
       cols = expr.size();
     } else EIGEN_IF_CONSTEXPR (PlainObjectType::ColsAtCompileTime == 1) {
-      eigen_assert(expr.rows() == 1 || expr.cols() == 1);
       rows = expr.size();
       cols = 1;
     }
diff --git a/Eigen/src/Core/Reshaped.h b/Eigen/src/Core/Reshaped.h
index 6cc21a8..f331e18 100644
--- a/Eigen/src/Core/Reshaped.h
+++ b/Eigen/src/Core/Reshaped.h
@@ -30,6 +30,9 @@
  * This class represents an expression of either a fixed-size or dynamic-size reshape.
  * It is the return type of DenseBase::reshaped(NRowsType,NColsType) and
  * most of the time this is the only way it is used.
+ * The source and reshape must have the same coefficient count. Incompatible
+ * fixed counts are rejected at compile time, or by a debug assertion when
+ * EIGEN_NO_STATIC_ASSERT is defined. Dynamic counts are checked in debug builds.
  *
  * If you want to directly manipulate reshaped expressions,
  * for instance if you want to write a function returning such an expression,
@@ -115,13 +118,15 @@
   EIGEN_DEVICE_FUNC constexpr inline Reshaped(XprType& xpr) : Impl(xpr) {
     EIGEN_STATIC_ASSERT(RowsAtCompileTime != Dynamic && ColsAtCompileTime != Dynamic,
                         THIS_METHOD_IS_ONLY_FOR_FIXED_SIZE)
-    eigen_assert(Rows * Cols == xpr.rows() * xpr.cols());
+    EIGEN_INTERNAL_ASSERT_RESHAPE_SIZE(xpr, Rows, Cols);
   }
 
   /** Dynamic-size constructor
    */
   EIGEN_DEVICE_FUNC constexpr inline Reshaped(XprType& xpr, Index reshapeRows, Index reshapeCols)
       : Impl(xpr, reshapeRows, reshapeCols) {
+    EIGEN_STATIC_ASSERT((internal::reshape_dimensions<XprType, Rows, Cols>::Compatible),
+                        YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
     eigen_assert((RowsAtCompileTime == Dynamic || RowsAtCompileTime == reshapeRows) &&
                  (ColsAtCompileTime == Dynamic || ColsAtCompileTime == reshapeCols));
     eigen_assert(reshapeRows * reshapeCols == xpr.rows() * xpr.cols());
diff --git a/Eigen/src/Core/ScaledPermutationMatrix.h b/Eigen/src/Core/ScaledPermutationMatrix.h
index 1d7dc38..a0d48b8 100644
--- a/Eigen/src/Core/ScaledPermutationMatrix.h
+++ b/Eigen/src/Core/ScaledPermutationMatrix.h
@@ -210,7 +210,7 @@
   typename internal::scaled_permutation_product_result<Derived, OtherDerived, StorageIndex>::type operator*(
       const DiagonalBase<OtherDerived>& other) const {
     using Result = typename internal::scaled_permutation_product_result<Derived, OtherDerived, StorageIndex>::type;
-    eigen_assert(rows() == other.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(*this, other);
     return Result(indices(), scales().cwiseProduct(other.diagonal()));
   }
 
@@ -219,7 +219,7 @@
   friend typename internal::scaled_permutation_product_result<OtherDerived, Derived, StorageIndex>::type operator*(
       const DiagonalBase<OtherDerived>& other, const ScaledPermutationBase& scaled) {
     using Result = typename internal::scaled_permutation_product_result<OtherDerived, Derived, StorageIndex>::type;
-    eigen_assert(other.rows() == scaled.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(other, scaled);
     Result result(scaled.rows());
     result.indices() = scaled.indices();
     for (Index k = 0; k < scaled.rows(); ++k)
@@ -230,7 +230,7 @@
   /** \returns the scaled permutation \c *this times the permutation \a other */
   template <typename OtherDerived>
   PlainObject operator*(const PermutationBase<OtherDerived>& other) const {
-    eigen_assert(rows() == other.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(*this, other);
     PlainObject result(rows());
     for (Index k = 0; k < rows(); ++k) {
       const Index source = other.indices().coeff(k);
@@ -243,7 +243,7 @@
   /** \returns the scaled permutation permutation \a other times \a scaled */
   template <typename OtherDerived>
   friend PlainObject operator*(const PermutationBase<OtherDerived>& other, const ScaledPermutationBase& scaled) {
-    eigen_assert(other.rows() == scaled.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(other, scaled);
     PlainObject result(scaled.rows());
     for (Index k = 0; k < scaled.rows(); ++k)
       result.indices().coeffRef(k) = StorageIndex(other.indices().coeff(scaled.indices().coeff(k)));
@@ -254,7 +254,7 @@
   /** \returns the scaled permutation \c *this times the inverse permutation \a other */
   template <typename OtherDerived>
   PlainObject operator*(const InverseImpl<OtherDerived, PermutationStorage>& other) const {
-    eigen_assert(rows() == other.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(*this, other);
     IndicesType inverse(rows());
     internal::invert_permutation_indices(other.derived().nestedExpression().indices(), inverse);
     PlainObject result(rows());
@@ -270,7 +270,7 @@
   template <typename OtherDerived>
   friend PlainObject operator*(const InverseImpl<OtherDerived, PermutationStorage>& other,
                                const ScaledPermutationBase& scaled) {
-    eigen_assert(other.rows() == scaled.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(other, scaled);
     IndicesType inverse(scaled.rows());
     internal::invert_permutation_indices(other.derived().nestedExpression().indices(), inverse);
     PlainObject result(scaled.rows());
@@ -284,7 +284,7 @@
   typename internal::scaled_permutation_product_result<Derived, OtherDerived, StorageIndex>::type operator*(
       const ScaledPermutationBase<OtherDerived>& other) const {
     using Result = typename internal::scaled_permutation_product_result<Derived, OtherDerived, StorageIndex>::type;
-    eigen_assert(rows() == other.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(*this, other);
     Result result(rows());
     for (Index k = 0; k < rows(); ++k) {
       const Index source = other.indices().coeff(k);
@@ -380,7 +380,7 @@
   EIGEN_DEVICE_FUNC ScaledPermutationMatrix(const PermutationBase<PermutationDerived>& permutation,
                                             const DiagonalBase<DiagonalDerived>& diagonal)
       : m_permutation(permutation), m_scales(diagonal.diagonal()) {
-    eigen_assert(permutation.rows() == diagonal.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(permutation, diagonal);
   }
 
   /** Constructs the scaled permutation with the given \a indices and \a scales. */
@@ -388,7 +388,7 @@
   EIGEN_DEVICE_FUNC ScaledPermutationMatrix(const MatrixBase<IndicesDerived>& indices,
                                             const MatrixBase<ScalesDerived>& scales)
       : m_permutation(indices), m_scales(scales) {
-    eigen_assert(indices.size() == scales.size());
+    EIGEN_INTERNAL_ASSERT_SAME_SIZE(indices, scales);
   }
 
   /** Converts the permutation \a permutation, all scales being one. */
@@ -465,7 +465,7 @@
   using Result =
       typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationDerived,
                                                    DiagonalDerived, typename PermutationDerived::StorageIndex>::type;
-  eigen_assert(permutation.rows() == diagonal.rows());
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(permutation, diagonal);
   return Result(permutation.indices(), diagonal.diagonal());
 }
 
@@ -478,7 +478,7 @@
   using Result =
       typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationDerived,
                                                    DiagonalDerived, typename PermutationDerived::StorageIndex>::type;
-  eigen_assert(permutation.rows() == diagonal.rows());
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(permutation, diagonal);
   Result result(permutation.rows());
   result.indices() = permutation.indices();
   for (Index k = 0; k < permutation.rows(); ++k)
@@ -496,7 +496,7 @@
   using Result =
       typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationType, DiagonalDerived,
                                                    typename PermutationType::StorageIndex>::type;
-  eigen_assert(inverse.rows() == diagonal.rows());
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(inverse, diagonal);
   Result result(inverse.rows());
   internal::invert_permutation_indices(inverse.derived().nestedExpression().indices(), result.indices());
   result.scales() = diagonal.diagonal();
@@ -513,7 +513,7 @@
   using Result =
       typename internal::scaled_permutation_result<typename DiagonalDerived::Scalar, PermutationType, DiagonalDerived,
                                                    typename PermutationType::StorageIndex>::type;
-  eigen_assert(inverse.rows() == diagonal.rows());
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(inverse, diagonal);
   Result result(inverse.rows());
   internal::invert_permutation_indices(inverse.derived().nestedExpression().indices(), result.indices());
   for (Index k = 0; k < inverse.rows(); ++k)
@@ -581,14 +581,14 @@
   static EIGEN_DEVICE_FUNC void run(
       DstXprType& dst, const SrcXprType& src,
       const internal::add_assign_op<typename DstXprType::Scalar, typename SrcXprType::Scalar>&) {
-    eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(dst, src);
     for (Index k = 0; k < src.rows(); ++k) dst.coeffRef(src.indices().coeff(k), k) += src.scales().coeff(k);
   }
 
   static EIGEN_DEVICE_FUNC void run(
       DstXprType& dst, const SrcXprType& src,
       const internal::sub_assign_op<typename DstXprType::Scalar, typename SrcXprType::Scalar>&) {
-    eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_SHAPE(dst, src);
     for (Index k = 0; k < src.rows(); ++k) dst.coeffRef(src.indices().coeff(k), k) -= src.scales().coeff(k);
   }
 };
diff --git a/Eigen/src/Core/SkewSymmetricMatrix3.h b/Eigen/src/Core/SkewSymmetricMatrix3.h
index 5dd4c58..6ed7578 100644
--- a/Eigen/src/Core/SkewSymmetricMatrix3.h
+++ b/Eigen/src/Core/SkewSymmetricMatrix3.h
@@ -203,7 +203,6 @@
   template <typename OtherDerived>
   EIGEN_DEVICE_FUNC friend EIGEN_STRONG_INLINE DenseSkewSumReturnType<OtherDerived> operator+(
       const MatrixBase<OtherDerived>& lhs, const SkewSymmetricBase& rhs) {
-    EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(OtherDerived, DenseMatrixType);
     return lhs.derived() + rhs.toDenseMatrix();
   }
 
@@ -220,7 +219,6 @@
   template <typename OtherDerived>
   EIGEN_DEVICE_FUNC friend EIGEN_STRONG_INLINE DenseSkewDifferenceReturnType<OtherDerived> operator-(
       const MatrixBase<OtherDerived>& lhs, const SkewSymmetricBase& rhs) {
-    EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(OtherDerived, DenseMatrixType);
     return lhs.derived() - rhs.toDenseMatrix();
   }
 
@@ -228,7 +226,6 @@
   template <typename OtherDerived>
   EIGEN_DEVICE_FUNC friend EIGEN_STRONG_INLINE DenseSkewDifferenceReturnType<OtherDerived> operator-(
       const SkewSymmetricBase& lhs, const MatrixBase<OtherDerived>& rhs) {
-    EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(OtherDerived, DenseMatrixType);
     return lhs.toDenseMatrix() - rhs.derived();
   }
 };
diff --git a/Eigen/src/Core/SolveTriangular.h b/Eigen/src/Core/SolveTriangular.h
index 25e7be5..fc19027 100644
--- a/Eigen/src/Core/SolveTriangular.h
+++ b/Eigen/src/Core/SolveTriangular.h
@@ -194,8 +194,13 @@
 EIGEN_DEVICE_FUNC void TriangularViewImpl<MatrixType, Mode, Dense>::solveInPlace(
     const MatrixBase<OtherDerived>& _other) const {
   OtherDerived& other = _other.const_cast_derived();
-  eigen_assert(derived().cols() == derived().rows() && ((Side == OnTheLeft && derived().cols() == other.rows()) ||
-                                                        (Side == OnTheRight && derived().cols() == other.cols())));
+  EIGEN_INTERNAL_ASSERT_SQUARE(derived());
+  // TODO(C++17): use if constexpr (Side == OnTheLeft) with PRODUCT_SHAPE/SAME_COLS checks directly.
+  EIGEN_INTERNAL_CHECK_SHAPE(
+      (Side == OnTheLeft ? derived().cols() == other.rows() : derived().cols() == other.cols()),
+      YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES,
+      std::conditional_t<Side == OnTheLeft, internal::product_dimensions<MatrixType, OtherDerived>,
+                         internal::matching_cols<MatrixType, OtherDerived>>);
   eigen_assert((!(int(Mode) & int(ZeroDiag))) && bool(int(Mode) & (int(Upper) | int(Lower))));
   // If solving for a 0x0 matrix, nothing to do, simply return.
   if (derived().cols() == 0) return;
diff --git a/Eigen/src/Core/SolverBase.h b/Eigen/src/Core/SolverBase.h
index 87e176b..d2938ac 100644
--- a/Eigen/src/Core/SolverBase.h
+++ b/Eigen/src/Core/SolverBase.h
@@ -145,10 +145,9 @@
  protected:
   template <bool Transpose_, typename Rhs>
   void _check_solve_assertion(const Rhs& b) const {
-    EIGEN_ONLY_USED_FOR_DEBUG(b);
     eigen_assert(derived().m_isInitialized && "Solver is not initialized.");
-    eigen_assert((Transpose_ ? derived().cols() : derived().rows()) == b.rows() &&
-                 "SolverBase::solve(): invalid number of rows of the right hand side matrix b");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(derived(), b, Transpose_,
+                                         "SolverBase::solve(): invalid number of rows of the right hand side matrix b");
   }
 };
 
diff --git a/Eigen/src/Core/Transpose.h b/Eigen/src/Core/Transpose.h
index cb32472..15cf038 100644
--- a/Eigen/src/Core/Transpose.h
+++ b/Eigen/src/Core/Transpose.h
@@ -246,7 +246,7 @@
   using Scalar = typename MatrixType::Scalar;
   using Packet = typename internal::packet_traits<typename MatrixType::Scalar>::type;
   const Index PacketSize = internal::packet_traits<Scalar>::size;
-  eigen_assert(m.rows() == m.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(m);
   int row_start = 0;
   for (; row_start + PacketSize <= m.rows(); row_start += PacketSize) {
     for (int col_start = row_start; col_start + PacketSize <= m.cols(); col_start += PacketSize) {
@@ -328,8 +328,11 @@
  * \sa transpose(), adjoint(), adjointInPlace() */
 template <typename Derived>
 EIGEN_DEVICE_FUNC inline void DenseBase<Derived>::transposeInPlace() {
-  eigen_assert((rows() == cols() || (RowsAtCompileTime == Dynamic && ColsAtCompileTime == Dynamic)) &&
-               "transposeInPlace() called on a non-square non-resizable matrix");
+  using ShapeRelation = std::conditional_t<RowsAtCompileTime == Dynamic && ColsAtCompileTime == Dynamic,
+                                           internal::dimension_relation<1, 1>, internal::square_dimensions<Derived>>;
+  EIGEN_INTERNAL_SHAPE_ASSERT(ShapeRelation::Proven,
+                              (rows() == cols() || (RowsAtCompileTime == Dynamic && ColsAtCompileTime == Dynamic)) &&
+                                  "transposeInPlace() called on a non-square non-resizable matrix");
   internal::inplace_transpose_selector<Derived>::run(derived());
 }
 
diff --git a/Eigen/src/Core/TriangularInPlace.h b/Eigen/src/Core/TriangularInPlace.h
index be22d2c..262ee69 100644
--- a/Eigen/src/Core/TriangularInPlace.h
+++ b/Eigen/src/Core/TriangularInPlace.h
@@ -81,7 +81,7 @@
 // against L22, and Eigen's TRMM runs closer to GEMM rate than its TRSM does on a 128-column operand.
 template <unsigned int Mode, typename MatrixType, std::enable_if_t<MatrixType::SizeAtCompileTime != 0, int> = 0>
 EIGEN_DEVICE_FUNC void triangular_inverse_lower(MatrixType& mat) {
-  eigen_assert(mat.rows() == mat.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(mat);
   const Index n = mat.rows();
   if (n < kTriangularInverseMinBlocked) {
     using Scalar = typename MatrixType::Scalar;
@@ -157,7 +157,7 @@
 // completed against the columns to its left before the trailing block rows consume the factor.
 template <typename MatrixType>
 void triangular_adjoint_square_lower(MatrixType& mat) {
-  eigen_assert(mat.rows() == mat.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(mat);
   const Index n = mat.rows();
   if (n < kAdjointSquareMinBlocked) {
     triangular_adjoint_square_unblocked(mat);
@@ -223,7 +223,7 @@
 EIGEN_DEVICE_FUNC void TriangularViewImpl<MatrixType, Mode, Dense>::inverseInPlace() {
   EIGEN_STATIC_ASSERT_LVALUE(MatrixType)
   EIGEN_STATIC_ASSERT((int(Mode) & int(Upper | Lower)) != 0 && (int(Mode) & int(ZeroDiag)) == 0, PROGRAMMING_ERROR)
-  eigen_assert(derived().rows() == derived().cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(derived());
   internal::triangular_inverse_selector<Mode>::run(derived().nestedExpression());
 }
 #endif
diff --git a/Eigen/src/Core/VectorwiseOp.h b/Eigen/src/Core/VectorwiseOp.h
index 4c95def..51d564d 100644
--- a/Eigen/src/Core/VectorwiseOp.h
+++ b/Eigen/src/Core/VectorwiseOp.h
@@ -383,7 +383,7 @@
    */
   template <typename BinaryOp>
   EIGEN_DEVICE_FUNC const typename ReduxReturnType<BinaryOp>::Type redux(const BinaryOp& func = BinaryOp()) const {
-    eigen_assert(redux_length() > 0 && "you are using an empty matrix");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_AXIS(m_matrix, isVertical);
     return typename ReduxReturnType<BinaryOp>::Type(_expression(), internal::member_redux<BinaryOp, Scalar>(func));
   }
 
@@ -421,7 +421,7 @@
    *
    * \sa DenseBase::minCoeff() */
   EIGEN_DEVICE_FUNC const MinCoeffReturnType minCoeff() const {
-    eigen_assert(redux_length() > 0 && "you are using an empty matrix");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_AXIS(m_matrix, isVertical);
     return MinCoeffReturnType(_expression());
   }
 
@@ -438,7 +438,7 @@
    *
    * \sa DenseBase::maxCoeff() */
   EIGEN_DEVICE_FUNC const MaxCoeffReturnType maxCoeff() const {
-    eigen_assert(redux_length() > 0 && "you are using an empty matrix");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_AXIS(m_matrix, isVertical);
     return MaxCoeffReturnType(_expression());
   }
 
diff --git a/Eigen/src/Core/products/GeneralMatrixMatrix.h b/Eigen/src/Core/products/GeneralMatrixMatrix.h
index f5a717b..842c37e 100644
--- a/Eigen/src/Core/products/GeneralMatrixMatrix.h
+++ b/Eigen/src/Core/products/GeneralMatrixMatrix.h
@@ -625,7 +625,8 @@
 
   template <typename Dest>
   static void scaleAndAddTo(Dest& dst, const Lhs& a_lhs, const Rhs& a_rhs, const Scalar& alpha) {
-    eigen_assert(dst.rows() == a_lhs.rows() && dst.cols() == a_rhs.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(dst, a_lhs);
+    EIGEN_INTERNAL_ASSERT_SAME_COLS(dst, a_rhs);
     if (a_lhs.cols() == 0 || a_lhs.rows() == 0 || a_rhs.cols() == 0) return;
 
     if (dst.cols() == 1) {
diff --git a/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h b/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h
index 4751a98..8edb29d 100644
--- a/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h
+++ b/Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h
@@ -303,8 +303,9 @@
     Index size = mat.cols();
     EIGEN_IF_CONSTEXPR (SkipDiag) size--;
     Index depth = actualLhs.cols();
-    eigen_assert(actualLhs.rows() == mat.rows() && actualRhs.cols() == mat.cols() &&
-                 actualLhs.cols() == actualRhs.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(actualLhs, mat);
+    EIGEN_INTERNAL_ASSERT_SAME_COLS(actualRhs, mat);
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(actualLhs, actualRhs);
     if (size <= 0 || depth == 0) return;
 
     using BlockingType =
@@ -336,7 +337,7 @@
 TriangularViewImpl<MatrixType_, Mode_, Dense>::_assignProduct(
     const ProductType& prod, const typename TriangularViewImpl<MatrixType_, Mode_, Dense>::Scalar& alpha, bool beta) {
   EIGEN_STATIC_ASSERT((Mode_ & UnitDiag) == 0, WRITING_TO_TRIANGULAR_PART_WITH_UNIT_DIAGONAL_IS_NOT_SUPPORTED);
-  eigen_assert(derived().nestedExpression().rows() == prod.rows() && derived().cols() == prod.cols());
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE(derived(), prod);
 
   // The rank-k and rank-1 kernels below update a square triangle.
   if (derived().rows() != derived().cols()) {
diff --git a/Eigen/src/Core/products/SelfadjointMatrixMatrix.h b/Eigen/src/Core/products/SelfadjointMatrixMatrix.h
index 52699e1..638d973 100644
--- a/Eigen/src/Core/products/SelfadjointMatrixMatrix.h
+++ b/Eigen/src/Core/products/SelfadjointMatrixMatrix.h
@@ -388,7 +388,8 @@
 
   template <typename Dest>
   static void run(Dest& dst, const Lhs& a_lhs, const Rhs& a_rhs, const Scalar& alpha) {
-    eigen_assert(dst.rows() == a_lhs.rows() && dst.cols() == a_rhs.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(dst, a_lhs);
+    EIGEN_INTERNAL_ASSERT_SAME_COLS(dst, a_rhs);
 
     add_const_on_value_type_t<ActualLhsType> lhs = LhsBlasTraits::extract(a_lhs);
     add_const_on_value_type_t<ActualRhsType> rhs = RhsBlasTraits::extract(a_rhs);
diff --git a/Eigen/src/Core/products/SelfadjointMatrixVector.h b/Eigen/src/Core/products/SelfadjointMatrixVector.h
index 85a08d0..212cdc6 100644
--- a/Eigen/src/Core/products/SelfadjointMatrixVector.h
+++ b/Eigen/src/Core/products/SelfadjointMatrixVector.h
@@ -339,7 +339,8 @@
     using ResScalar = typename Dest::Scalar;
     using RhsScalar = typename Rhs::Scalar;
 
-    eigen_assert(dest.rows() == a_lhs.rows() && dest.cols() == a_rhs.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(dest, a_lhs);
+    EIGEN_INTERNAL_ASSERT_SAME_COLS(dest, a_rhs);
 
     add_const_on_value_type_t<ActualLhsType> lhs = LhsBlasTraits::extract(a_lhs);
     add_const_on_value_type_t<ActualRhsType> rhs = RhsBlasTraits::extract(a_rhs);
diff --git a/Eigen/src/Core/products/SelfadjointProduct.h b/Eigen/src/Core/products/SelfadjointProduct.h
index a58925e..a709dcf 100644
--- a/Eigen/src/Core/products/SelfadjointProduct.h
+++ b/Eigen/src/Core/products/SelfadjointProduct.h
@@ -201,7 +201,7 @@
 
     Index size = mat.cols();
     Index depth = actualOther.cols();
-    eigen_assert(actualOther.rows() == size);
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(mat, actualOther);
     if (size == 0 || depth == 0) return;
 
     using BlockingType =
diff --git a/Eigen/src/Core/products/TriangularMatrixVector.h b/Eigen/src/Core/products/TriangularMatrixVector.h
index 70d571c..d7bba5c 100644
--- a/Eigen/src/Core/products/TriangularMatrixVector.h
+++ b/Eigen/src/Core/products/TriangularMatrixVector.h
@@ -229,7 +229,8 @@
 struct triangular_product_impl<Mode, true, Lhs, false, Rhs, true> {
   template <typename Dest>
   static void run(Dest& dst, const Lhs& lhs, const Rhs& rhs, const typename Dest::Scalar& alpha) {
-    eigen_assert(dst.rows() == lhs.rows() && dst.cols() == rhs.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(dst, lhs);
+    EIGEN_INTERNAL_ASSERT_SAME_COLS(dst, rhs);
 
     internal::trmv_selector<Mode, (int(internal::traits<Lhs>::Flags) & RowMajorBit) ? RowMajor : ColMajor>::run(
         lhs, rhs, dst, alpha);
@@ -240,7 +241,8 @@
 struct triangular_product_impl<Mode, false, Lhs, true, Rhs, false> {
   template <typename Dest>
   static void run(Dest& dst, const Lhs& lhs, const Rhs& rhs, const typename Dest::Scalar& alpha) {
-    eigen_assert(dst.rows() == lhs.rows() && dst.cols() == rhs.cols());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(dst, lhs);
+    EIGEN_INTERNAL_ASSERT_SAME_COLS(dst, rhs);
 
     Transpose<Dest> dstT(dst);
     internal::trmv_selector<(Mode & (UnitDiag | ZeroDiag)) | ((Mode & Lower) ? Upper : Lower),
diff --git a/Eigen/src/Core/util/ShapeChecks.h b/Eigen/src/Core/util/ShapeChecks.h
new file mode 100644
index 0000000..d725eef
--- /dev/null
+++ b/Eigen/src/Core/util/ShapeChecks.h
@@ -0,0 +1,267 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+
+#ifndef EIGEN_SHAPE_CHECKS_H
+#define EIGEN_SHAPE_CHECKS_H
+
+// IWYU pragma: private
+#include "../InternalHeaderCheck.h"
+
+namespace Eigen {
+namespace internal {
+
+template <typename T, typename = void>
+struct shape_expression {
+  using type = T;
+};
+template <typename T>
+struct shape_expression<T, void_t<decltype(std::declval<const T&>().derived())>> {
+  using type = remove_all_t<decltype(std::declval<const T&>().derived())>;
+};
+
+// EigenBase extensions may omit either compile-time dimension.
+template <typename T, typename = void>
+struct shape_rows : std::integral_constant<int, Dynamic> {};
+template <typename T>
+struct shape_rows<T, void_t<decltype(traits<remove_all_t<T>>::RowsAtCompileTime)>>
+    : std::integral_constant<int, traits<remove_all_t<T>>::RowsAtCompileTime> {};
+
+template <typename T, typename = void>
+struct shape_cols : std::integral_constant<int, Dynamic> {};
+template <typename T>
+struct shape_cols<T, void_t<decltype(traits<remove_all_t<T>>::ColsAtCompileTime)>>
+    : std::integral_constant<int, traits<remove_all_t<T>>::ColsAtCompileTime> {};
+
+template <typename T>
+struct compile_time_shape {
+  static constexpr int Rows = shape_rows<T>::value;
+  static constexpr int Cols = shape_cols<T>::value;
+  static constexpr int Size = size_at_compile_time(Rows, Cols);
+};
+
+template <int Left, int Right>
+struct dimension_relation {
+  static constexpr bool Known = Left != Dynamic && Right != Dynamic;
+  static constexpr bool Compatible = !Known || Left == Right;
+  static constexpr bool Proven = Known && Compatible;
+};
+
+template <typename Left, typename Right>
+using matching_rows = dimension_relation<compile_time_shape<Left>::Rows, compile_time_shape<Right>::Rows>;
+template <typename Left, typename Right>
+using matching_cols = dimension_relation<compile_time_shape<Left>::Cols, compile_time_shape<Right>::Cols>;
+template <typename Left, typename Right>
+using matching_sizes = dimension_relation<compile_time_shape<Left>::Size, compile_time_shape<Right>::Size>;
+template <typename SizeExpr, typename MatrixExpr, bool RowAxis>
+using size_matches_axis =
+    dimension_relation<compile_time_shape<SizeExpr>::Size,
+                       RowAxis ? compile_time_shape<MatrixExpr>::Rows : compile_time_shape<MatrixExpr>::Cols>;
+template <typename Left, typename Right>
+struct matching_shapes {
+  using Rows = matching_rows<Left, Right>;
+  using Cols = matching_cols<Left, Right>;
+  static constexpr bool Known = Rows::Known && Cols::Known;
+  // Preserve the empty-expression exception of EIGEN_PREDICATE_SAME_MATRIX_SIZE.
+  static constexpr bool Compatible = (compile_time_shape<Left>::Size == 0 && compile_time_shape<Right>::Size == 0) ||
+                                     (Rows::Compatible && Cols::Compatible);
+  static constexpr bool Proven = Rows::Proven && Cols::Proven;
+};
+template <typename Left, typename Right>
+using product_dimensions = dimension_relation<compile_time_shape<Left>::Cols, compile_time_shape<Right>::Rows>;
+template <typename T>
+using square_dimensions = dimension_relation<compile_time_shape<T>::Rows, compile_time_shape<T>::Cols>;
+
+template <typename T, int Rows, int Cols>
+using reshape_dimensions = dimension_relation<size_at_compile_time(Rows, Cols), compile_time_shape<T>::Size>;
+
+template <typename Solver, typename Rhs, bool Transposed = false>
+using solver_rhs_dimensions =
+    dimension_relation<Transposed ? compile_time_shape<Solver>::Cols : compile_time_shape<Solver>::Rows,
+                       compile_time_shape<Rhs>::Rows>;
+
+template <typename T>
+struct vector_dimensions {
+  static constexpr bool Proven = compile_time_shape<T>::Rows == 1 || compile_time_shape<T>::Cols == 1;
+  static constexpr bool Known =
+      Proven || (compile_time_shape<T>::Rows != Dynamic && compile_time_shape<T>::Cols != Dynamic);
+  static constexpr bool Compatible = !Known || Proven;
+};
+
+template <int Extent>
+struct nonempty_extent {
+  static constexpr bool Known = Extent != Dynamic;
+  static constexpr bool Compatible = !Known || Extent > 0;
+  static constexpr bool Proven = Known && Compatible;
+};
+
+template <typename T>
+using nonempty_dimensions = nonempty_extent<compile_time_shape<T>::Size>;
+
+template <typename T, int Rows, int Cols>
+struct fixed_dimensions {
+  using RowRelation = dimension_relation<compile_time_shape<T>::Rows, Rows>;
+  using ColRelation = dimension_relation<compile_time_shape<T>::Cols, Cols>;
+  static constexpr bool Known = RowRelation::Known && ColRelation::Known;
+  static constexpr bool Compatible = RowRelation::Compatible && ColRelation::Compatible;
+  static constexpr bool Proven = RowRelation::Proven && ColRelation::Proven;
+};
+
+}  // namespace internal
+}  // namespace Eigen
+
+// These macros are private implementation helpers, not a public assertion API.
+// Only EIGEN_INTERNAL_DEBUG_SHAPE_ASSERT uses internal-debug assertion semantics.
+// Keep the runtime expression lazy and preserve assertion overrides.
+// In particular, a proven check must not call dimension accessors even at -O0.
+// An unevaluated use prevents unused-parameter warnings when assertions are disabled.
+#define EIGEN_INTERNAL_SHAPE_ASSERT(Proven, Condition) \
+  do {                                                 \
+    EIGEN_IF_CONSTEXPR (!(Proven)) {                   \
+      (void)sizeof(static_cast<bool>(Condition));      \
+      eigen_assert(Condition);                         \
+    }                                                  \
+  } while (false)
+
+#define EIGEN_INTERNAL_DEBUG_SHAPE_ASSERT(Proven, Condition) \
+  do {                                                       \
+    EIGEN_IF_CONSTEXPR (!(Proven)) {                         \
+      (void)sizeof(static_cast<bool>(Condition));            \
+      eigen_internal_assert(Condition);                      \
+    }                                                        \
+  } while (false)
+
+// Infer the concrete expression without calling derived() or dimension accessors.
+#define EIGEN_INTERNAL_SHAPE_TYPE(Xpr) \
+  typename Eigen::internal::shape_expression<Eigen::internal::remove_all_t<decltype(Xpr)>>::type
+
+// The relation is shared by the static diagnostic and the lazy debug check.
+#define EIGEN_INTERNAL_CHECK_SHAPE(Condition, Message, ...) \
+  do {                                                      \
+    using ShapeRelation = __VA_ARGS__;                      \
+    EIGEN_STATIC_ASSERT(ShapeRelation::Compatible, Message) \
+    EIGEN_IF_CONSTEXPR (!ShapeRelation::Proven) {           \
+      (void)sizeof(static_cast<bool>(Condition));           \
+      eigen_assert(Condition);                              \
+    }                                                       \
+  } while (false)
+
+#define EIGEN_INTERNAL_ASSERT_SAME_ROWS_MSG(Lhs, Rhs, Message)                          \
+  EIGEN_INTERNAL_CHECK_SHAPE(                                                           \
+      (Lhs).rows() == (Rhs).rows() && (Message), YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES, \
+      Eigen::internal::matching_rows<EIGEN_INTERNAL_SHAPE_TYPE(Lhs), EIGEN_INTERNAL_SHAPE_TYPE(Rhs)>)
+#define EIGEN_INTERNAL_ASSERT_SAME_ROWS(Lhs, Rhs) \
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS_MSG(Lhs, Rhs, "Both expressions must have the same number of rows")
+
+#define EIGEN_INTERNAL_ASSERT_SAME_SHAPE_MSG(Lhs, Rhs, Message)                                           \
+  do {                                                                                                    \
+    using ShapeRelation =                                                                                 \
+        Eigen::internal::matching_shapes<EIGEN_INTERNAL_SHAPE_TYPE(Lhs), EIGEN_INTERNAL_SHAPE_TYPE(Rhs)>; \
+    EIGEN_STATIC_ASSERT(ShapeRelation::Compatible, YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)                 \
+    EIGEN_INTERNAL_SHAPE_ASSERT(ShapeRelation::Rows::Proven, (Lhs).rows() == (Rhs).rows() && (Message));  \
+    EIGEN_INTERNAL_SHAPE_ASSERT(ShapeRelation::Cols::Proven, (Lhs).cols() == (Rhs).cols() && (Message));  \
+  } while (false)
+#define EIGEN_INTERNAL_ASSERT_SAME_SHAPE(Lhs, Rhs) \
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE_MSG(Lhs, Rhs, "Both expressions must have the same dimensions")
+
+#define EIGEN_INTERNAL_ASSERT_SAME_COLS_MSG(Lhs, Rhs, Message)                          \
+  EIGEN_INTERNAL_CHECK_SHAPE(                                                           \
+      (Lhs).cols() == (Rhs).cols() && (Message), YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES, \
+      Eigen::internal::matching_cols<EIGEN_INTERNAL_SHAPE_TYPE(Lhs), EIGEN_INTERNAL_SHAPE_TYPE(Rhs)>)
+#define EIGEN_INTERNAL_ASSERT_SAME_COLS(Lhs, Rhs) \
+  EIGEN_INTERNAL_ASSERT_SAME_COLS_MSG(Lhs, Rhs, "Both expressions must have the same number of columns")
+
+#define EIGEN_INTERNAL_ASSERT_SAME_SIZE_MSG(Lhs, Rhs, Message)                         \
+  EIGEN_INTERNAL_CHECK_SHAPE(                                                          \
+      (Lhs).size() == (Rhs).size() && (Message), YOU_MIXED_VECTORS_OF_DIFFERENT_SIZES, \
+      Eigen::internal::matching_sizes<EIGEN_INTERNAL_SHAPE_TYPE(Lhs), EIGEN_INTERNAL_SHAPE_TYPE(Rhs)>)
+#define EIGEN_INTERNAL_ASSERT_SAME_SIZE(Lhs, Rhs) \
+  EIGEN_INTERNAL_ASSERT_SAME_SIZE_MSG(Lhs, Rhs, "Both expressions must have the same number of coefficients")
+
+#define EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(Lhs, Rhs, Message)       \
+  EIGEN_INTERNAL_CHECK_SHAPE(                                            \
+      (Lhs).cols() == (Rhs).rows() && (Message), INVALID_MATRIX_PRODUCT, \
+      Eigen::internal::product_dimensions<EIGEN_INTERNAL_SHAPE_TYPE(Lhs), EIGEN_INTERNAL_SHAPE_TYPE(Rhs)>)
+#define EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(Lhs, Rhs) \
+  EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(            \
+      Lhs, Rhs,                                       \
+      "invalid matrix product; "                      \
+      "if you wanted a coeff-wise or a dot product use the respective explicit functions")
+
+// SizeExpr must use coefficient-count size semantics (rows * cols), not permutation/transposition order.
+// Select the axis in the relation so C++14 never instantiates a check for the unused dimension.
+#define EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS_MSG(SizeExpr, MatrixExpr, RowAxis, Message)                          \
+  EIGEN_INTERNAL_CHECK_SHAPE(                                                                                        \
+      (SizeExpr).size() == ((RowAxis) ? (MatrixExpr).rows() : (MatrixExpr).cols()) && (Message),                     \
+      YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES,                                                                         \
+      Eigen::internal::size_matches_axis<EIGEN_INTERNAL_SHAPE_TYPE(SizeExpr), EIGEN_INTERNAL_SHAPE_TYPE(MatrixExpr), \
+                                         RowAxis>)
+#define EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS(SizeExpr, MatrixExpr, RowAxis) \
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS_MSG(SizeExpr, MatrixExpr, RowAxis,   \
+                                              "Coefficient count must match the selected matrix dimension")
+#define EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS_MSG(SizeExpr, MatrixExpr, Message) \
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS_MSG(SizeExpr, MatrixExpr, true, Message)
+#define EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS_MSG(SizeExpr, MatrixExpr, Message) \
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS_MSG(SizeExpr, MatrixExpr, false, Message)
+#define EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(SizeExpr, MatrixExpr) \
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS(SizeExpr, MatrixExpr, true)
+#define EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS(SizeExpr, MatrixExpr) \
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS(SizeExpr, MatrixExpr, false)
+
+#define EIGEN_INTERNAL_ASSERT_SQUARE_MSG(Xpr, Message)                                                      \
+  EIGEN_INTERNAL_SHAPE_ASSERT((Eigen::internal::square_dimensions<EIGEN_INTERNAL_SHAPE_TYPE(Xpr)>::Proven), \
+                              (Xpr).rows() == (Xpr).cols() && (Message))
+#define EIGEN_INTERNAL_ASSERT_SQUARE(Xpr) \
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(Xpr, "This operation requires a square matrix")
+
+// TODO(C++17): guard the vector check with if constexpr (Enabled) instead of selecting a dummy relation.
+#define EIGEN_INTERNAL_ASSERT_VECTOR_IF(Enabled, Xpr)                                                 \
+  EIGEN_INTERNAL_CHECK_SHAPE(                                                                         \
+      ((Xpr).rows() == 1 || (Xpr).cols() == 1) && "This operation requires a vector",                 \
+      YOU_TRIED_CALLING_A_VECTOR_METHOD_ON_A_MATRIX,                                                  \
+      std::conditional_t<Enabled, Eigen::internal::vector_dimensions<EIGEN_INTERNAL_SHAPE_TYPE(Xpr)>, \
+                         Eigen::internal::dimension_relation<1, 1>>)
+#define EIGEN_INTERNAL_ASSERT_VECTOR(Xpr) EIGEN_INTERNAL_ASSERT_VECTOR_IF(true, Xpr)
+
+#define EIGEN_INTERNAL_ASSERT_SCALAR(Xpr)                                                        \
+  EIGEN_INTERNAL_CHECK_SHAPE(                                                                    \
+      ((Xpr).rows() == 1 && (Xpr).cols() == 1) && "This operation requires a 1-by-1 expression", \
+      THIS_METHOD_IS_ONLY_FOR_1x1_EXPRESSIONS,                                                   \
+      Eigen::internal::fixed_dimensions<EIGEN_INTERNAL_SHAPE_TYPE(Xpr), 1, 1>)
+
+#define EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(Xpr, Message)                                                      \
+  EIGEN_INTERNAL_SHAPE_ASSERT((Eigen::internal::nonempty_dimensions<EIGEN_INTERNAL_SHAPE_TYPE(Xpr)>::Proven), \
+                              (Xpr).rows() > 0 && (Xpr).cols() > 0 && (Message))
+#define EIGEN_INTERNAL_ASSERT_NONEMPTY(Xpr) EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(Xpr, "you are using an empty matrix")
+
+#define EIGEN_INTERNAL_ASSERT_NONEMPTY_AXIS(Xpr, Vertical)                                                          \
+  do {                                                                                                              \
+    using ShapeRelation = Eigen::internal::nonempty_extent<                                                         \
+        (Vertical) ? Eigen::internal::compile_time_shape<EIGEN_INTERNAL_SHAPE_TYPE(Xpr)>::Rows                      \
+                   : Eigen::internal::compile_time_shape<EIGEN_INTERNAL_SHAPE_TYPE(Xpr)>::Cols>;                    \
+    EIGEN_INTERNAL_SHAPE_ASSERT(ShapeRelation::Proven,                                                              \
+                                ((Vertical) ? (Xpr).rows() : (Xpr).cols()) > 0 && "you are using an empty matrix"); \
+  } while (false)
+
+// Size must be the cached coefficient count of Xpr, not an allocation capacity.
+#define EIGEN_INTERNAL_ASSERT_NONEMPTY_SIZE_MSG(Xpr, Size, Message)                                           \
+  EIGEN_INTERNAL_SHAPE_ASSERT((Eigen::internal::nonempty_dimensions<EIGEN_INTERNAL_SHAPE_TYPE(Xpr)>::Proven), \
+                              (Size) > 0 && (Message))
+#define EIGEN_INTERNAL_ASSERT_NONEMPTY_SIZE(Xpr, Size) \
+  EIGEN_INTERNAL_ASSERT_NONEMPTY_SIZE_MSG(Xpr, Size, "you are using an empty matrix")
+
+#define EIGEN_INTERNAL_ASSERT_RESHAPE_SIZE(Xpr, Rows, Cols)                                                   \
+  EIGEN_INTERNAL_CHECK_SHAPE(                                                                                 \
+      (Rows) * (Cols) == (Xpr).rows() * (Xpr).cols() && "Reshaping must preserve the number of coefficients", \
+      YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES,                                                                  \
+      Eigen::internal::reshape_dimensions<EIGEN_INTERNAL_SHAPE_TYPE(Xpr), Rows, Cols>)
+
+#define EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(Solver, Rhs, Transposed, Message)                                \
+  EIGEN_INTERNAL_CHECK_SHAPE(((Transposed) ? (Solver).cols() : (Solver).rows()) == (Rhs).rows() && (Message), \
+                             YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES,                                           \
+                             Eigen::internal::solver_rhs_dimensions<EIGEN_INTERNAL_SHAPE_TYPE(Solver),        \
+                                                                    EIGEN_INTERNAL_SHAPE_TYPE(Rhs), Transposed>)
+#define EIGEN_INTERNAL_ASSERT_SOLVER_RHS(Solver, Rhs, Transposed) \
+  EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(Solver, Rhs, Transposed,   \
+                                       "solve(): invalid number of rows of the right hand side")
+
+#endif  // EIGEN_SHAPE_CHECKS_H
diff --git a/Eigen/src/Eigenvalues/ComplexEigenSolver.h b/Eigen/src/Eigenvalues/ComplexEigenSolver.h
index fd41a90..d77c790 100644
--- a/Eigen/src/Eigenvalues/ComplexEigenSolver.h
+++ b/Eigen/src/Eigenvalues/ComplexEigenSolver.h
@@ -259,7 +259,7 @@
 ComplexEigenSolver<MatrixType>& ComplexEigenSolver<MatrixType>::compute(const EigenBase<InputType>& matrix,
                                                                         bool computeEigenvectors) {
   // this code is inspired from Jampack
-  eigen_assert(matrix.cols() == matrix.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matrix);
 
   // Do a complex Schur decomposition, A = U T U^*
   // The eigenvalues are on the diagonal of T.
diff --git a/Eigen/src/Eigenvalues/ComplexQZ.h b/Eigen/src/Eigenvalues/ComplexQZ.h
index 12b5b9a..f3a0ff0 100644
--- a/Eigen/src/Eigenvalues/ComplexQZ.h
+++ b/Eigen/src/Eigenvalues/ComplexQZ.h
@@ -272,9 +272,8 @@
 template <typename MatrixType_>
 template <typename InputTypeA, typename InputTypeB>
 void ComplexQZ<MatrixType_>::compute(const EigenBase<InputTypeA>& A, const EigenBase<InputTypeB>& B, bool computeQZ) {
-  eigen_assert(A.rows() == A.cols() && "A is not a square matrix");
-  eigen_assert(A.rows() == B.rows() && A.rows() == B.cols() &&
-               "B is not a square matrix or B is not of the same size as A");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(A, "A is not a square matrix");
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE_MSG(A, B, "B is not a square matrix or B is not of the same size as A");
   // Copy A and B, these will be the matrices on which we operate later
   m_S = A.derived();
   m_T = B.derived();
@@ -287,8 +286,8 @@
   m_computeQZ = computeQZ;
   m_n = m_S.rows();
 
-  eigen_assert(m_n == m_S.cols() && "A is not a square matrix");
-  eigen_assert(m_n == m_T.rows() && m_n == m_T.cols() && "B is not a square matrix or B is not of the same size as A");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_S, "A is not a square matrix");
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE_MSG(m_S, m_T, "B is not a square matrix or B is not of the same size as A");
 
   m_isInitialized = true;
   m_global_iter = 0;
@@ -417,8 +416,8 @@
 void ComplexQZ<MatrixType>::computeSparse(const SparseMatrixType_& A, const SparseMatrixType_& B, bool computeQZ) {
   m_computeQZ = computeQZ;
   m_n = A.rows();
-  eigen_assert(m_n == A.cols() && "A is not a square matrix");
-  eigen_assert(m_n == B.rows() && m_n == B.cols() && "B is not a square matrix or B is not of the same size as A");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(A, "A is not a square matrix");
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE_MSG(A, B, "B is not a square matrix or B is not of the same size as A");
   m_isInitialized = true;
   m_global_iter = 0;
   m_info = Success;
diff --git a/Eigen/src/Eigenvalues/ComplexSchur.h b/Eigen/src/Eigenvalues/ComplexSchur.h
index 7aa3815..c27d813 100644
--- a/Eigen/src/Eigenvalues/ComplexSchur.h
+++ b/Eigen/src/Eigenvalues/ComplexSchur.h
@@ -349,7 +349,7 @@
 template <typename InputType>
 ComplexSchur<MatrixType>& ComplexSchur<MatrixType>::compute(const EigenBase<InputType>& matrix, bool computeU) {
   m_matUisUptodate = false;
-  eigen_assert(matrix.cols() == matrix.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matrix);
 
   if (matrix.cols() <= 1) {
     m_matT = matrix.derived().template cast<ComplexScalar>();
@@ -381,7 +381,7 @@
 template <typename MatrixType>
 ComplexSchur<MatrixType>& ComplexSchur<MatrixType>::computeInPlace(bool computeU) {
   m_matUisUptodate = false;
-  eigen_assert(m_matT.cols() == m_matT.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(m_matT);
 
   if (m_matT.cols() <= 1) {
     if (computeU) m_matU = ComplexMatrixType::Identity(m_matT.rows(), m_matT.cols());
diff --git a/Eigen/src/Eigenvalues/EigenSolver.h b/Eigen/src/Eigenvalues/EigenSolver.h
index 7aefd98..1d434cd 100644
--- a/Eigen/src/Eigenvalues/EigenSolver.h
+++ b/Eigen/src/Eigenvalues/EigenSolver.h
@@ -394,7 +394,7 @@
 EigenSolver<MatrixType>& EigenSolver<MatrixType>::compute(const EigenBase<InputType>& matrix,
                                                           bool computeEigenvectors) {
   check_template_parameters();
-  eigen_assert(matrix.cols() == matrix.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matrix);
 
   // Reduce to real Schur form.
   m_realSchur.compute(matrix.derived(), computeEigenvectors);
diff --git a/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h b/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h
index 00ba959..7dfa043 100644
--- a/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h
+++ b/Eigen/src/Eigenvalues/GeneralizedEigenSolver.h
@@ -307,7 +307,8 @@
 GeneralizedEigenSolver<MatrixType>& GeneralizedEigenSolver<MatrixType>::compute(const EigenBase<InputTypeA>& A,
                                                                                 const EigenBase<InputTypeB>& B,
                                                                                 bool computeEigenvectors) {
-  eigen_assert(A.cols() == A.rows() && B.cols() == A.rows() && B.cols() == B.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(A);
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE(A, B);
   // Reduce to generalized real Schur form:
   // A = Q S Z and B = Q T Z
   m_realQZ.compute(A.derived(), B.derived(), computeEigenvectors);
diff --git a/Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h b/Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h
index 92ce974..77e8054 100644
--- a/Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h
+++ b/Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h
@@ -196,7 +196,8 @@
 template <typename InputTypeA, typename InputTypeB>
 GeneralizedSelfAdjointEigenSolver<MatrixType>& GeneralizedSelfAdjointEigenSolver<MatrixType>::compute(
     const EigenBase<InputTypeA>& matA, const EigenBase<InputTypeB>& matB, int options) {
-  eigen_assert(matA.cols() == matA.rows() && matB.rows() == matA.rows() && matB.cols() == matB.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matA);
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE(matA, matB);
 
   // Compute the cholesky decomposition of matB = L L' = U'U
   m_cholB.compute(matB.derived());
@@ -209,7 +210,8 @@
 template <typename MatrixType>
 GeneralizedSelfAdjointEigenSolver<MatrixType>& GeneralizedSelfAdjointEigenSolver<MatrixType>::computeInPlace(
     int options) {
-  eigen_assert(m_matC.cols() == m_matC.rows() && m_cholB.rows() == m_matC.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(m_matC);
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(m_cholB, m_matC);
   eigen_assert((options & ~(EigVecMask | GenEigMask)) == 0 && (options & EigVecMask) != EigVecMask &&
                ((options & GenEigMask) == 0 || (options & GenEigMask) == Ax_lBx || (options & GenEigMask) == ABx_lx ||
                 (options & GenEigMask) == BAx_lx) &&
diff --git a/Eigen/src/Eigenvalues/HessenbergDecomposition.h b/Eigen/src/Eigenvalues/HessenbergDecomposition.h
index c0d792a..189a159 100644
--- a/Eigen/src/Eigenvalues/HessenbergDecomposition.h
+++ b/Eigen/src/Eigenvalues/HessenbergDecomposition.h
@@ -300,7 +300,7 @@
 void hessenberg_decomposition_inplace(MatrixType& matA, CoeffVectorType& hCoeffs, WorkspaceType& temp) {
   using Scalar = typename MatrixType::Scalar;
   using RealScalar = typename NumTraits<Scalar>::Real;
-  eigen_assert(matA.rows() == matA.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matA);
   Index n = matA.rows();
   temp.resize(n);
   for (Index i = 0; i < n - 1; ++i) {
diff --git a/Eigen/src/Eigenvalues/RealQZ.h b/Eigen/src/Eigenvalues/RealQZ.h
index e5cd1f1..e44ff26 100644
--- a/Eigen/src/Eigenvalues/RealQZ.h
+++ b/Eigen/src/Eigenvalues/RealQZ.h
@@ -536,8 +536,8 @@
 template <typename InputTypeA, typename InputTypeB>
 RealQZ<MatrixType>& RealQZ<MatrixType>::compute(const EigenBase<InputTypeA>& A_in, const EigenBase<InputTypeB>& B_in,
                                                 bool computeQZ) {
-  eigen_assert(A_in.rows() == A_in.cols() && B_in.rows() == A_in.cols() && B_in.cols() == A_in.cols() &&
-               "Need square matrices of the same dimension");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(A_in, "Need square matrices of the same dimension");
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE_MSG(A_in, B_in, "Need square matrices of the same dimension");
   m_S = A_in.derived();
   m_T = B_in.derived();
   return computeInPlace(computeQZ);
@@ -548,8 +548,8 @@
 RealQZ<MatrixType>& RealQZ<MatrixType>::computeInPlace(bool computeQZ) {
   const Index dim = m_S.cols();
 
-  eigen_assert(m_S.rows() == dim && m_T.rows() == dim && m_T.cols() == dim &&
-               "Need square matrices of the same dimension");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_S, "Need square matrices of the same dimension");
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE_MSG(m_S, m_T, "Need square matrices of the same dimension");
 
   m_isInitialized = true;
   m_computeQZ = computeQZ;
diff --git a/Eigen/src/Eigenvalues/RealSchur.h b/Eigen/src/Eigenvalues/RealSchur.h
index a8fb2df..a47fb3a 100644
--- a/Eigen/src/Eigenvalues/RealSchur.h
+++ b/Eigen/src/Eigenvalues/RealSchur.h
@@ -308,7 +308,7 @@
 template <typename MatrixType>
 template <typename InputType>
 RealSchur<MatrixType>& RealSchur<MatrixType>::compute(const EigenBase<InputType>& matrix, bool computeU) {
-  eigen_assert(matrix.cols() == matrix.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matrix);
   const Index size = matrix.rows();
   if (usePaddedWorkspace(size)) {
     WorkspaceMatrix storage(size + (MatrixType::IsRowMajor ? 0 : 1), size + (MatrixType::IsRowMajor ? 1 : 0));
@@ -342,7 +342,7 @@
 RealSchur<MatrixType>& RealSchur<MatrixType>::computeInPlace(TMatrix& matT, bool computeU) {
   const Scalar considerAsZero = (std::numeric_limits<Scalar>::min)();
   const Index n = matT.rows();
-  eigen_assert(matT.cols() == n);
+  EIGEN_INTERNAL_ASSERT_SQUARE(matT);
 
   const Scalar maxCoeff = n == 0 ? Scalar(0) : matT.cwiseAbs().template maxCoeff<PropagateNaN>();
   if (!(numext::isfinite)(maxCoeff)) {
diff --git a/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h b/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h
index 4c4eff7..de6a142 100644
--- a/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h
+++ b/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h
@@ -480,14 +480,14 @@
 EIGEN_DEVICE_FUNC SelfAdjointEigenSolver<MatrixType>& SelfAdjointEigenSolver<MatrixType>::compute(
     const EigenBase<InputType>& a_matrix, int options) {
   const InputType& matrix(a_matrix.derived());
-  eigen_assert(matrix.cols() == matrix.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matrix);
   m_eivec = matrix.template triangularView<Lower>();
   return computeInPlace(options);
 }
 
 template <typename MatrixType>
 EIGEN_DEVICE_FUNC SelfAdjointEigenSolver<MatrixType>& SelfAdjointEigenSolver<MatrixType>::computeInPlace(int options) {
-  eigen_assert(m_eivec.cols() == m_eivec.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(m_eivec);
   eigen_assert((options & ~(EigVecMask | GenEigMask)) == 0 && (options & EigVecMask) != EigVecMask &&
                "invalid option parameter");
   bool computeEigenvectors = (options & ComputeEigenvectors) == ComputeEigenvectors;
@@ -992,7 +992,8 @@
   }
 
   EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE static void run(SolverType& solver, const MatrixType& mat, int options) {
-    eigen_assert(mat.rows() == Size && mat.cols() == Size);
+    EIGEN_INTERNAL_CHECK_SHAPE(mat.rows() == Size && mat.cols() == Size, YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES,
+                               internal::fixed_dimensions<MatrixType, Size, Size>);
     eigen_assert((options & ~(EigVecMask | GenEigMask)) == 0 && (options & EigVecMask) != EigVecMask &&
                  "invalid option parameter");
     PlainMatrixType scaledMat = mat.template selfadjointView<Lower>();
diff --git a/Eigen/src/Eigenvalues/Tridiagonalization.h b/Eigen/src/Eigenvalues/Tridiagonalization.h
index a21e573..ba640c6 100644
--- a/Eigen/src/Eigenvalues/Tridiagonalization.h
+++ b/Eigen/src/Eigenvalues/Tridiagonalization.h
@@ -354,7 +354,7 @@
   using Scalar = typename MatrixType::Scalar;
   using RealScalar = typename MatrixType::RealScalar;
   Index n = matA.rows();
-  eigen_assert(n == matA.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matA);
   eigen_assert(n == hCoeffs.size() + 1 || n == 1);
 
   for (Index i = 0; i < n - 1; ++i) {
@@ -397,7 +397,7 @@
   using Scalar = typename MatrixType::Scalar;
   using RealScalar = typename MatrixType::RealScalar;
   const Index n = matA.rows();
-  eigen_assert(n == matA.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matA);
   eigen_assert(n == hCoeffs.size() + 1);
   eigen_assert(nb >= 2 && nb < n);
 
@@ -525,7 +525,7 @@
  */
 template <typename MatrixType, typename CoeffVectorType>
 EIGEN_DEVICE_FUNC void tridiagonalization_inplace(MatrixType& matA, CoeffVectorType& hCoeffs) {
-  eigen_assert(matA.rows() == matA.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(matA);
   eigen_assert(matA.rows() == hCoeffs.size() + 1 || matA.rows() == 1);
 
 #if !defined(EIGEN_GPU_COMPILE_PHASE)
@@ -590,7 +590,9 @@
           typename WorkSpaceType>
 EIGEN_DEVICE_FUNC void tridiagonalization_inplace(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag,
                                                   CoeffVectorType& hcoeffs, WorkSpaceType& workspace, bool extractQ) {
-  eigen_assert(mat.cols() == mat.rows() && diag.size() == mat.rows() && subdiag.size() == mat.rows() - 1);
+  EIGEN_INTERNAL_ASSERT_SQUARE(mat);
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(diag, mat);
+  eigen_assert(subdiag.size() == mat.rows() - 1);
   tridiagonalization_inplace_selector<MatrixType>::run(mat, diag, subdiag, hcoeffs, workspace, extractQ);
 }
 
diff --git a/Eigen/src/Geometry/AngleAxis.h b/Eigen/src/Geometry/AngleAxis.h
index c190c52..7bd7a6a 100644
--- a/Eigen/src/Geometry/AngleAxis.h
+++ b/Eigen/src/Geometry/AngleAxis.h
@@ -137,7 +137,7 @@
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Vector3 _transformVector(const OtherVectorType& v) const {
     EIGEN_USING_STD(sin)
     EIGEN_USING_STD(cos)
-    eigen_assert(v.size() == 3);
+    EIGEN_INTERNAL_ASSERT_SAME_SIZE(v, m_axis);
 
     // Rodrigues' rotation formula: v' = v*cos(θ) + (k×v)*sin(θ) + k*(k·v)*(1-cos(θ))
     const Vector3 v3(v);
@@ -231,7 +231,9 @@
   EIGEN_STATIC_ASSERT(
       (std::is_same<Scalar, typename Derived::Scalar>::value),
       YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
-  eigen_assert(mat.cols() == 3 && mat.rows() == 3);
+  // operator= also instantiates this path for quaternion coefficients in C++14.
+  // TODO(C++17): use fixed-size if constexpr dispatch in operator= before adding a static 3-by-3 check here.
+  EIGEN_INTERNAL_SHAPE_ASSERT((internal::fixed_dimensions<Derived, 3, 3>::Proven), mat.cols() == 3 && mat.rows() == 3);
 
   // Compute a (non-unit) quaternion proportional to the rotation using
   // Shoemake's algorithm (shared with Quaternion::operator=(MatrixBase)),
diff --git a/Eigen/src/Geometry/Homogeneous.h b/Eigen/src/Geometry/Homogeneous.h
index 365adc6..195c129 100644
--- a/Eigen/src/Geometry/Homogeneous.h
+++ b/Eigen/src/Geometry/Homogeneous.h
@@ -97,6 +97,9 @@
     return Product<Transform<Scalar, Dim, Mode, Options>, Homogeneous>(lhs, rhs);
   }
 
+  /** Reduces all coefficients, including the appended ones. An empty vector's
+   * homogeneous expression contains one coefficient equal to one.
+   */
   template <typename Func>
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE typename internal::result_of<Func(Scalar, Scalar)>::type redux(
       const Func& func) const {
diff --git a/Eigen/src/Householder/HouseholderSequence.h b/Eigen/src/Householder/HouseholderSequence.h
index 99f081c..39e985c 100644
--- a/Eigen/src/Householder/HouseholderSequence.h
+++ b/Eigen/src/Householder/HouseholderSequence.h
@@ -477,7 +477,7 @@
   typename internal::permutation_type_as_matrix_type<Scalar, OtherDerived>::Type operator*(
       const PermutationBase<OtherDerived>& other) const {
     using ResultType = typename internal::permutation_type_as_matrix_type<Scalar, OtherDerived>::Type;
-    eigen_assert(cols() == other.rows());
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(*this, other);
     ResultType res = ResultType::Identity(rows(), rows());
     applyThisOnTheLeft(res, true);
     // The permutation product runs in place when its operand is the destination.
@@ -490,7 +490,7 @@
   typename internal::permutation_type_as_matrix_type<Scalar, Inverse<OtherDerived>>::Type operator*(
       const InverseImpl<OtherDerived, PermutationStorage>& other) const {
     using ResultType = typename internal::permutation_type_as_matrix_type<Scalar, Inverse<OtherDerived>>::Type;
-    eigen_assert(cols() == other.rows());
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(*this, other);
     ResultType res = ResultType::Identity(rows(), rows());
     applyThisOnTheLeft(res, true);
     res.noalias() = res * other.derived();
@@ -506,7 +506,7 @@
   // the identity. Other inputs take the full path.
   template <typename ResultType, typename OtherDerived>
   ResultType applyToUpperTriangularOnTheLeft(const EigenBase<OtherDerived>& other, bool upperTriangular) const {
-    eigen_assert(cols() == other.rows());
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(*this, other);
     ResultType res;
     internal::assign_householder_operand(res, other);
     applyThisOnTheLeft(res, upperTriangular && res.rows() == res.cols());
@@ -608,7 +608,7 @@
 typename internal::matrix_type_times_scalar_type<typename VectorsType::Scalar, OtherDerived>::Type operator*(
     const DiagonalBase<OtherDerived>& other, const HouseholderSequence<VectorsType, CoeffsType, Side>& h) {
   using ResultType = typename internal::matrix_type_times_scalar_type<typename VectorsType::Scalar, OtherDerived>::Type;
-  eigen_assert(other.cols() == h.rows());
+  EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(other, h);
   ResultType res = ResultType::Identity(h.rows(), h.rows());
   h.applyThisOnTheLeft(res, true);
   // The lazy diagonal product multiplies from the left, D(i,i) * H(i,j), as a noncommutative scalar requires, and
@@ -642,7 +642,7 @@
     const PermutationBase<OtherDerived>& other, const HouseholderSequence<VectorsType, CoeffsType, Side>& h) {
   using ResultType =
       typename internal::permutation_type_as_matrix_type<typename VectorsType::Scalar, OtherDerived>::Type;
-  eigen_assert(other.cols() == h.rows());
+  EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(other, h);
   ResultType res = ResultType::Identity(h.rows(), h.rows());
   h.applyThisOnTheLeft(res, true);
   // The permutation product runs in place when its operand is the destination.
@@ -657,7 +657,7 @@
     const HouseholderSequence<VectorsType, CoeffsType, Side>& h) {
   using ResultType =
       typename internal::permutation_type_as_matrix_type<typename VectorsType::Scalar, Inverse<OtherDerived>>::Type;
-  eigen_assert(other.cols() == h.rows());
+  EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(other, h);
   ResultType res = ResultType::Identity(h.rows(), h.rows());
   h.applyThisOnTheLeft(res, true);
   res.noalias() = other.derived() * res;
diff --git a/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h b/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h
index a8e1659..5b30e4e 100644
--- a/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h
+++ b/Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h
@@ -89,8 +89,8 @@
   template <typename Rhs>
   inline Solve<DiagonalPreconditioner, Rhs> solve(const MatrixBase<Rhs>& b) const {
     eigen_assert(m_isInitialized && "DiagonalPreconditioner is not initialized.");
-    eigen_assert(m_invdiag.size() == b.rows() &&
-                 "DiagonalPreconditioner::solve(): invalid number of rows of the right hand side matrix b");
+    EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS_MSG(
+        m_invdiag, b, "DiagonalPreconditioner::solve(): invalid number of rows of the right hand side matrix b");
     return Solve<DiagonalPreconditioner, Rhs>(*this, b.derived());
   }
 
diff --git a/Eigen/src/IterativeLinearSolvers/DGMRES.h b/Eigen/src/IterativeLinearSolvers/DGMRES.h
index a4f341b..0e7ed97 100644
--- a/Eigen/src/IterativeLinearSolvers/DGMRES.h
+++ b/Eigen/src/IterativeLinearSolvers/DGMRES.h
@@ -39,7 +39,7 @@
  */
 template <typename VectorType, typename IndexType>
 void sortWithPermutation(VectorType& vec, IndexType& perm, typename IndexType::Scalar& ncut) {
-  eigen_assert(vec.size() == perm.size());
+  EIGEN_INTERNAL_ASSERT_SAME_SIZE(vec, perm);
   for (Index k = 0; k < ncut; k++) {
     bool flag = false;
     for (Index j = 0; j < vec.size() - 1; j++) {
diff --git a/Eigen/src/IterativeLinearSolvers/IncompleteLUT.h b/Eigen/src/IterativeLinearSolvers/IncompleteLUT.h
index df2fed3..11fed19 100644
--- a/Eigen/src/IterativeLinearSolvers/IncompleteLUT.h
+++ b/Eigen/src/IterativeLinearSolvers/IncompleteLUT.h
@@ -393,7 +393,7 @@
 template <typename Scalar, typename StorageIndex>
 template <typename MatrixType_>
 void IncompleteLUT<Scalar, StorageIndex>::analyzePattern(const MatrixType_& amat) {
-  eigen_assert((amat.rows() == amat.cols()) && "The factorization should be done on a square matrix");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(amat, "The factorization should be done on a square matrix");
   // 1. Compute a static row permutation that makes the diagonal structurally
   //    nonzero. This is a workaround for the lack of partial pivoting in ILUT.
   //    For matrices that already have a nonzero diagonal, this returns the
@@ -433,7 +433,7 @@
   using std::sqrt;
   using std::swap;
 
-  eigen_assert((amat.rows() == amat.cols()) && "The factorization should be done on a square matrix");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(amat, "The factorization should be done on a square matrix");
   Index n = amat.cols();  // Size of the matrix
   m_lu.resize(n, n);
   // Declare Working vectors and variables
diff --git a/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h b/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h
index 690d9dd..bcf1d7d 100644
--- a/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h
+++ b/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h
@@ -276,7 +276,8 @@
   template <typename Rhs, typename Guess>
   inline const SolveWithGuess<Derived, Rhs, Guess> solveWithGuess(const MatrixBase<Rhs>& b, const Guess& x0) const {
     eigen_assert(m_isInitialized && "Solver is not initialized.");
-    eigen_assert(derived().rows() == b.rows() && "solve(): invalid number of rows of the right hand side matrix b");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(derived(), b, false,
+                                         "solve(): invalid number of rows of the right hand side matrix b");
     return SolveWithGuess<Derived, Rhs, Guess>(derived(), b.derived(), x0);
   }
 
@@ -293,8 +294,8 @@
   template <typename Rhs, typename Dest>
   void solveWithGuessInPlace(const Rhs& b, Dest& x) const {
     eigen_assert(m_isInitialized && "Solver is not initialized.");
-    eigen_assert(derived().rows() == b.rows() &&
-                 "solveWithGuessInPlace(): invalid number of rows of the right hand side b");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(derived(), b, false,
+                                         "solveWithGuessInPlace(): invalid number of rows of the right hand side b");
     derived()._solve_vector_with_guess_impl(b, x);
   }
 
@@ -307,7 +308,7 @@
   /** \internal */
   template <typename Rhs, typename DestDerived>
   void _solve_with_guess_impl(const Rhs& b, SparseMatrixBase<DestDerived>& aDest) const {
-    eigen_assert(rows() == b.rows());
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS(derived(), b, false);
 
     Index rhsCols = b.cols();
     Index size = b.rows();
@@ -340,7 +341,7 @@
   template <typename Rhs, typename DestDerived>
   std::enable_if_t<Rhs::ColsAtCompileTime != 1 && DestDerived::ColsAtCompileTime != 1> _solve_with_guess_impl(
       const Rhs& b, MatrixBase<DestDerived>& aDest) const {
-    eigen_assert(rows() == b.rows());
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS(derived(), b, false);
 
     Index rhsCols = b.cols();
     DestDerived& dest(aDest.derived());
diff --git a/Eigen/src/Jacobi/Jacobi.h b/Eigen/src/Jacobi/Jacobi.h
index c4dac91..d40ac70 100644
--- a/Eigen/src/Jacobi/Jacobi.h
+++ b/Eigen/src/Jacobi/Jacobi.h
@@ -488,7 +488,7 @@
   constexpr bool Vectorizable = (int(evaluator<VectorX>::Flags) & int(evaluator<VectorY>::Flags) & PacketAccessBit) &&
                                 (int(packet_traits<Scalar>::size) == int(packet_traits<OtherScalar>::size));
 
-  eigen_assert(xpr_x.size() == xpr_y.size());
+  EIGEN_INTERNAL_ASSERT_SAME_SIZE(xpr_x, xpr_y);
   Index size = xpr_x.size();
   Index incrx = xpr_x.derived().innerStride();
   Index incry = xpr_y.derived().innerStride();
diff --git a/Eigen/src/LU/Determinant.h b/Eigen/src/LU/Determinant.h
index ac9e05a..927270c 100644
--- a/Eigen/src/LU/Determinant.h
+++ b/Eigen/src/LU/Determinant.h
@@ -86,7 +86,7 @@
  */
 template <typename Derived>
 EIGEN_DEVICE_FUNC inline typename internal::traits<Derived>::Scalar MatrixBase<Derived>::determinant() const {
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   using Nested = typename internal::nested_eval<Derived, Base::RowsAtCompileTime>::type;
   return internal::determinant_impl<internal::remove_all_t<Nested>>::run(derived());
 }
diff --git a/Eigen/src/LU/FullPivLU.h b/Eigen/src/LU/FullPivLU.h
index 1857d27..bf7a9a0 100644
--- a/Eigen/src/LU/FullPivLU.h
+++ b/Eigen/src/LU/FullPivLU.h
@@ -349,7 +349,7 @@
    */
   inline Inverse<FullPivLU> inverse() const {
     eigen_assert(m_isInitialized && "LU is not initialized.");
-    eigen_assert(m_lu.rows() == m_lu.cols() && "You can't take the inverse of a non-square matrix!");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_lu, "You can't take the inverse of a non-square matrix!");
     return Inverse<FullPivLU>(*this);
   }
 
@@ -509,7 +509,7 @@
 template <typename MatrixType, typename PermutationIndex>
 typename internal::traits<MatrixType>::Scalar FullPivLU<MatrixType, PermutationIndex>::determinant() const {
   eigen_assert(m_isInitialized && "LU is not initialized.");
-  eigen_assert(m_lu.rows() == m_lu.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_lu, "You can't take the determinant of a non-square matrix!");
   return Scalar(m_det_pq) * Scalar(m_lu.diagonal().prod());
 }
 
@@ -517,7 +517,7 @@
 typename FullPivLU<MatrixType, PermutationIndex>::RealScalar FullPivLU<MatrixType, PermutationIndex>::absDeterminant()
     const {
   eigen_assert(m_isInitialized && "LU is not initialized.");
-  eigen_assert(m_lu.rows() == m_lu.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_lu, "You can't take the determinant of a non-square matrix!");
   return isInjective() ? numext::abs(m_lu.diagonal().prod()) : RealScalar(0);
 }
 
@@ -525,7 +525,7 @@
 typename FullPivLU<MatrixType, PermutationIndex>::RealScalar
 FullPivLU<MatrixType, PermutationIndex>::logAbsDeterminant() const {
   eigen_assert(m_isInitialized && "LU is not initialized.");
-  eigen_assert(m_lu.rows() == m_lu.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_lu, "You can't take the determinant of a non-square matrix!");
   return isInjective() ? m_lu.diagonal().cwiseAbs().array().log().sum() : -NumTraits<RealScalar>::infinity();
 }
 
@@ -533,7 +533,7 @@
 typename FullPivLU<MatrixType, PermutationIndex>::Scalar FullPivLU<MatrixType, PermutationIndex>::signDeterminant()
     const {
   eigen_assert(m_isInitialized && "LU is not initialized.");
-  eigen_assert(m_lu.rows() == m_lu.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_lu, "You can't take the determinant of a non-square matrix!");
   return isInjective() ? Scalar(m_det_pq) * m_lu.diagonal().array().sign().prod() : Scalar(0);
 }
 
diff --git a/Eigen/src/LU/InverseImpl.h b/Eigen/src/LU/InverseImpl.h
index 56327e7..aa4e05d 100644
--- a/Eigen/src/LU/InverseImpl.h
+++ b/Eigen/src/LU/InverseImpl.h
@@ -279,7 +279,7 @@
 template <typename Derived>
 EIGEN_DEVICE_FUNC inline Inverse<Derived> MatrixBase<Derived>::inverse() const {
   EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::IsInteger, THIS_FUNCTION_IS_NOT_FOR_INTEGER_NUMERIC_TYPES)
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   return Inverse<Derived>(derived());
 }
 
@@ -310,7 +310,7 @@
                                                                bool& invertible,
                                                                const RealScalar& absDeterminantThreshold) const {
   EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Derived, ResultType)
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   inverse.resize(rows(), cols());
   // for 2x2, it's worth giving a chance to avoid evaluating.
   // for larger sizes, evaluating has negligible cost and limits code size.
diff --git a/Eigen/src/LU/PartialPivLU.h b/Eigen/src/LU/PartialPivLU.h
index a26c774..8cac1d4 100644
--- a/Eigen/src/LU/PartialPivLU.h
+++ b/Eigen/src/LU/PartialPivLU.h
@@ -291,7 +291,7 @@
      * Step 3: update  c = P^-1 c.
      */
 
-    eigen_assert(rhs.rows() == m_lu.cols());
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS(m_lu, rhs, true);
 
     // Step 1
     dst = m_lu.template triangularView<Upper>().transpose().template conjugateIf<Conjugate>().solve(rhs);
@@ -606,7 +606,7 @@
     nb_transpositions = 0;
     return;
   }
-  eigen_assert(lu.cols() == row_transpositions.size());
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS(row_transpositions.indices(), lu);
   eigen_assert(row_transpositions.size() < 2 ||
                (&row_transpositions.coeffRef(1) - &row_transpositions.coeffRef(0)) == 1);
 
@@ -651,7 +651,7 @@
   else
     m_l1_norm = RealScalar(0);
 
-  eigen_assert(m_lu.rows() == m_lu.cols() && "PartialPivLU is only for square (and moreover invertible) matrices");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_lu, "PartialPivLU is only for square (and moreover invertible) matrices");
   const Index size = m_lu.rows();
 
   m_rowsTranspositions.resize(size);
diff --git a/Eigen/src/QR/ColPivHouseholderQR.h b/Eigen/src/QR/ColPivHouseholderQR.h
index e4abaf8..dca45f7 100644
--- a/Eigen/src/QR/ColPivHouseholderQR.h
+++ b/Eigen/src/QR/ColPivHouseholderQR.h
@@ -328,7 +328,7 @@
 template <typename MatrixType, typename PermutationIndex>
 typename MatrixType::Scalar ColPivHouseholderQR<MatrixType, PermutationIndex>::determinant() const {
   eigen_assert(m_isInitialized && "HouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   Scalar detQ;
   internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
   return isInjective() ? (detQ * Scalar(m_det_p)) * m_qr.diagonal().prod() : Scalar(0);
@@ -338,21 +338,21 @@
 typename MatrixType::RealScalar ColPivHouseholderQR<MatrixType, PermutationIndex>::absDeterminant() const {
   using std::abs;
   eigen_assert(m_isInitialized && "ColPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   return isInjective() ? abs(m_qr.diagonal().prod()) : RealScalar(0);
 }
 
 template <typename MatrixType, typename PermutationIndex>
 typename MatrixType::RealScalar ColPivHouseholderQR<MatrixType, PermutationIndex>::logAbsDeterminant() const {
   eigen_assert(m_isInitialized && "ColPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   return isInjective() ? m_qr.diagonal().cwiseAbs().array().log().sum() : -NumTraits<RealScalar>::infinity();
 }
 
 template <typename MatrixType, typename PermutationIndex>
 typename MatrixType::Scalar ColPivHouseholderQR<MatrixType, PermutationIndex>::signDeterminant() const {
   eigen_assert(m_isInitialized && "ColPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   Scalar detQ;
   internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
   return isInjective() ? (detQ * Scalar(m_det_p)) * m_qr.diagonal().array().sign().prod() : Scalar(0);
diff --git a/Eigen/src/QR/CompleteOrthogonalDecomposition.h b/Eigen/src/QR/CompleteOrthogonalDecomposition.h
index 9b6a520..426b351 100644
--- a/Eigen/src/QR/CompleteOrthogonalDecomposition.h
+++ b/Eigen/src/QR/CompleteOrthogonalDecomposition.h
@@ -187,10 +187,10 @@
 
   template <bool Transpose_, typename Rhs>
   void _check_solve_assertion(const Rhs& b) const {
-    EIGEN_ONLY_USED_FOR_DEBUG(b);
     eigen_assert(m_cpqr.m_isInitialized && "CompleteOrthogonalDecomposition is not initialized.");
-    eigen_assert((Transpose_ ? this->cols() : this->rows()) == b.rows() &&
-                 "CompleteOrthogonalDecomposition::solve(): invalid number of rows of the right hand side matrix b");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(
+        *this, b, Transpose_,
+        "CompleteOrthogonalDecomposition::solve(): invalid number of rows of the right hand side matrix b");
   }
 
   void computeInPlace();
diff --git a/Eigen/src/QR/FullPivHouseholderQR.h b/Eigen/src/QR/FullPivHouseholderQR.h
index 880115e..3de6e0c 100644
--- a/Eigen/src/QR/FullPivHouseholderQR.h
+++ b/Eigen/src/QR/FullPivHouseholderQR.h
@@ -338,7 +338,7 @@
 template <typename MatrixType, typename PermutationIndex>
 typename MatrixType::Scalar FullPivHouseholderQR<MatrixType, PermutationIndex>::determinant() const {
   eigen_assert(m_isInitialized && "HouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   Scalar detQ;
   internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
   return isInjective() ? (detQ * Scalar(m_det_p)) * m_qr.diagonal().prod() : Scalar(0);
@@ -348,21 +348,21 @@
 typename MatrixType::RealScalar FullPivHouseholderQR<MatrixType, PermutationIndex>::absDeterminant() const {
   using std::abs;
   eigen_assert(m_isInitialized && "FullPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   return isInjective() ? abs(m_qr.diagonal().prod()) : RealScalar(0);
 }
 
 template <typename MatrixType, typename PermutationIndex>
 typename MatrixType::RealScalar FullPivHouseholderQR<MatrixType, PermutationIndex>::logAbsDeterminant() const {
   eigen_assert(m_isInitialized && "FullPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   return isInjective() ? m_qr.diagonal().cwiseAbs().array().log().sum() : -NumTraits<RealScalar>::infinity();
 }
 
 template <typename MatrixType, typename PermutationIndex>
 typename MatrixType::Scalar FullPivHouseholderQR<MatrixType, PermutationIndex>::signDeterminant() const {
   eigen_assert(m_isInitialized && "FullPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   Scalar detQ;
   internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
   return isInjective() ? (detQ * Scalar(m_det_p)) * m_qr.diagonal().array().sign().prod() : Scalar(0);
diff --git a/Eigen/src/QR/HouseholderQR.h b/Eigen/src/QR/HouseholderQR.h
index 1cbb180..4a48441 100644
--- a/Eigen/src/QR/HouseholderQR.h
+++ b/Eigen/src/QR/HouseholderQR.h
@@ -338,7 +338,7 @@
 template <typename MatrixType>
 typename MatrixType::Scalar HouseholderQR<MatrixType>::determinant() const {
   eigen_assert(m_isInitialized && "HouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   Scalar detQ;
   internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
   return m_qr.diagonal().prod() * detQ;
@@ -348,21 +348,21 @@
 typename MatrixType::RealScalar HouseholderQR<MatrixType>::absDeterminant() const {
   using std::abs;
   eigen_assert(m_isInitialized && "HouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   return abs(m_qr.diagonal().prod());
 }
 
 template <typename MatrixType>
 typename MatrixType::RealScalar HouseholderQR<MatrixType>::logAbsDeterminant() const {
   eigen_assert(m_isInitialized && "HouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   return m_qr.diagonal().cwiseAbs().array().log().sum();
 }
 
 template <typename MatrixType>
 typename MatrixType::Scalar HouseholderQR<MatrixType>::signDeterminant() const {
   eigen_assert(m_isInitialized && "HouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   Scalar detQ;
   internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
   return detQ * m_qr.diagonal().array().sign().prod();
@@ -424,8 +424,8 @@
 
   eigen_assert(k < mat.cols());
   eigen_assert(k < rows);
-  eigen_assert(hCoeffs.size() == mat.cols());
-  eigen_assert(newColumn.size() == rows);
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS(hCoeffs, mat);
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(newColumn, mat);
   eigen_assert(tempData);
 
   // Store new column in mat at column k
diff --git a/Eigen/src/QR/RandColPivHouseholderQR.h b/Eigen/src/QR/RandColPivHouseholderQR.h
index 71cff02..ea4f3b3 100644
--- a/Eigen/src/QR/RandColPivHouseholderQR.h
+++ b/Eigen/src/QR/RandColPivHouseholderQR.h
@@ -398,7 +398,7 @@
 template <typename MatrixType, typename PermutationIndex>
 typename MatrixType::Scalar RandColPivHouseholderQR<MatrixType, PermutationIndex>::determinant() const {
   eigen_assert(m_isInitialized && "RandColPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   Scalar detQ;
   internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
   return isInjective() ? (detQ * Scalar(m_det_p)) * m_qr.diagonal().prod() : Scalar(0);
@@ -408,21 +408,21 @@
 typename MatrixType::RealScalar RandColPivHouseholderQR<MatrixType, PermutationIndex>::absDeterminant() const {
   using std::abs;
   eigen_assert(m_isInitialized && "RandColPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   return isInjective() ? abs(m_qr.diagonal().prod()) : RealScalar(0);
 }
 
 template <typename MatrixType, typename PermutationIndex>
 typename MatrixType::RealScalar RandColPivHouseholderQR<MatrixType, PermutationIndex>::logAbsDeterminant() const {
   eigen_assert(m_isInitialized && "RandColPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   return isInjective() ? m_qr.diagonal().cwiseAbs().array().log().sum() : -NumTraits<RealScalar>::infinity();
 }
 
 template <typename MatrixType, typename PermutationIndex>
 typename MatrixType::Scalar RandColPivHouseholderQR<MatrixType, PermutationIndex>::signDeterminant() const {
   eigen_assert(m_isInitialized && "RandColPivHouseholderQR is not initialized.");
-  eigen_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_qr, "You can't take the determinant of a non-square matrix!");
   Scalar detQ;
   internal::householder_determinant<HCoeffsType, Scalar, NumTraits<Scalar>::IsComplex>::run(m_hCoeffs, detQ);
   return isInjective() ? (detQ * Scalar(m_det_p)) * m_qr.diagonal().array().sign().prod() : Scalar(0);
diff --git a/Eigen/src/SVD/SVDBase.h b/Eigen/src/SVD/SVDBase.h
index db95114..724c6d3 100644
--- a/Eigen/src/SVD/SVDBase.h
+++ b/Eigen/src/SVD/SVDBase.h
@@ -320,12 +320,11 @@
 
   template <bool Transpose_, typename Rhs>
   void _check_solve_assertion(const Rhs& b) const {
-    EIGEN_ONLY_USED_FOR_DEBUG(b);
     _check_compute_assertions();
     eigen_assert(computeU() && computeV() &&
                  "SVDBase::solve(): Both unitaries U and V are required to be computed (thin unitaries suffice).");
-    eigen_assert((Transpose_ ? cols() : rows()) == b.rows() &&
-                 "SVDBase::solve(): invalid number of rows of the right hand side matrix b");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(derived(), b, Transpose_,
+                                         "SVDBase::solve(): invalid number of rows of the right hand side matrix b");
   }
 
   // return true if already allocated
diff --git a/Eigen/src/SparseCholesky/SimplicialCholesky.h b/Eigen/src/SparseCholesky/SimplicialCholesky.h
index 92e751b..6eb1c14 100644
--- a/Eigen/src/SparseCholesky/SimplicialCholesky.h
+++ b/Eigen/src/SparseCholesky/SimplicialCholesky.h
@@ -180,7 +180,7 @@
     eigen_assert(m_factorizationIsOk &&
                  "The decomposition is not in a valid state for solving, you must first call either compute() or "
                  "symbolic()/numeric()");
-    eigen_assert(m_matrix.rows() == b.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(m_matrix, b);
 
     if (m_info != Success) return;
 
@@ -211,7 +211,7 @@
   /** Computes the sparse Cholesky decomposition of \a matrix */
   template <bool DoLDLT, bool NonHermitian>
   void compute(const MatrixType& matrix) {
-    eigen_assert(matrix.rows() == matrix.cols());
+    EIGEN_INTERNAL_ASSERT_SQUARE(matrix);
     Index size = matrix.cols();
     CholMatrixType tmp(size, size);
     ConstCholMatrixPtr pmat;
@@ -222,7 +222,7 @@
 
   template <bool DoLDLT, bool NonHermitian>
   void factorize(const MatrixType& a) {
-    eigen_assert(a.rows() == a.cols());
+    EIGEN_INTERNAL_ASSERT_SQUARE(a);
     Index size = a.cols();
     CholMatrixType tmp(size, size);
     ConstCholMatrixPtr pmat;
@@ -245,7 +245,7 @@
 
   template <bool DoLDLT, bool NonHermitian>
   void analyzePattern(const MatrixType& a) {
-    eigen_assert(a.rows() == a.cols());
+    EIGEN_INTERNAL_ASSERT_SQUARE(a);
     Index size = a.cols();
     CholMatrixType tmp(size, size);
     ConstCholMatrixPtr pmat;
@@ -930,7 +930,7 @@
     eigen_assert(Base::m_factorizationIsOk &&
                  "The decomposition is not in a valid state for solving, you must first call either compute() or "
                  "symbolic()/numeric()");
-    eigen_assert(Base::m_matrix.rows() == b.rows());
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS(Base::m_matrix, b);
 
     if (Base::m_info != Success) return;
 
@@ -984,7 +984,7 @@
 template <typename Derived>
 template <bool NonHermitian>
 void SimplicialCholeskyBase<Derived>::ordering(const MatrixType& a, ConstCholMatrixPtr& pmat, CholMatrixType& ap) {
-  eigen_assert(a.rows() == a.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(a);
   const Index size = a.rows();
   pmat = &ap;
   // Note that ordering methods compute the inverse permutation
diff --git a/Eigen/src/SparseCholesky/SimplicialCholesky_impl.h b/Eigen/src/SparseCholesky/SimplicialCholesky_impl.h
index 85d363b..7ad42e3 100644
--- a/Eigen/src/SparseCholesky/SimplicialCholesky_impl.h
+++ b/Eigen/src/SparseCholesky/SimplicialCholesky_impl.h
@@ -323,7 +323,7 @@
   const StorageIndex size = StorageIndex(ap.rows());
 
   eigen_assert(m_analysisIsOk && "You must first call analyzePattern()");
-  eigen_assert(ap.rows() == ap.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(ap);
   eigen_assert(m_parent.size() == size);
   eigen_assert(m_workSpace.size() >= 3 * size);
 
diff --git a/Eigen/src/SparseCore/BlockSparseMatrix.h b/Eigen/src/SparseCore/BlockSparseMatrix.h
index a9ef4b3..f046b9b 100644
--- a/Eigen/src/SparseCore/BlockSparseMatrix.h
+++ b/Eigen/src/SparseCore/BlockSparseMatrix.h
@@ -1328,7 +1328,7 @@
     EIGEN_STATIC_ASSERT(
         (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
         YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
-    eigen_assert(m_matrix.cols() == rhs.rows() && "BlockSparseTriangularView * Dense: dimension mismatch");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(m_matrix, rhs, "BlockSparseTriangularView * Dense: dimension mismatch");
     using ResultType = Matrix<Scalar, Dynamic, OtherDerived::ColsAtCompileTime>;
     ResultType result = ResultType::Zero(m_matrix.rows(), rhs.cols());
     for (Index out = 0; out < m_matrix.m_blockOuterSize; ++out) {
@@ -1356,7 +1356,7 @@
     EIGEN_STATIC_ASSERT(
         (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
         YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
-    eigen_assert(lhs.cols() == tri.m_matrix.rows() && "Dense * BlockSparseTriangularView: dimension mismatch");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(lhs, tri.m_matrix, "Dense * BlockSparseTriangularView: dimension mismatch");
     constexpr bool isRM = BSM::IsRowMajor;
     using ResultType = Matrix<Scalar, OtherDerived::RowsAtCompileTime, Dynamic>;
     // BSM befriends BlockSparseTriangularView, but GCC <= 11 and MSVC do not
@@ -1434,7 +1434,7 @@
     constexpr int DiagMode = IsUpper ? Upper : Lower;
     constexpr bool diagFirst = (IsUpper == BSM::IsRowMajor);
     Index nb = m_matrix.blockCols();  // == blockRows() for square matrices
-    eigen_assert(x.rows() == m_matrix.rows() && "solveInPlace: size mismatch");
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS_MSG(x, m_matrix, "solveInPlace: size mismatch");
 
     const StorageIndex* innerPtr = m_matrix.innerIndexPtr();
     const StorageIndex* outerPtr = m_matrix.outerIndexPtr();
@@ -1484,7 +1484,7 @@
     constexpr int DiagMode = IsUpper ? Upper : Lower;
     constexpr bool diagFirst = (IsUpper == BSM::IsRowMajor);
     Index nb = m_matrix.blockCols();  // == blockRows() for square matrices
-    eigen_assert(x.rows() == m_matrix.rows() && "solveInPlace: size mismatch");
+    EIGEN_INTERNAL_ASSERT_SAME_ROWS_MSG(x, m_matrix, "solveInPlace: size mismatch");
 
     const StorageIndex* innerPtr = m_matrix.innerIndexPtr();
     const StorageIndex* outerPtr = m_matrix.outerIndexPtr();
@@ -1680,7 +1680,7 @@
     EIGEN_STATIC_ASSERT(
         (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
         YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
-    eigen_assert(m_matrix.cols() == rhs.rows() && "BlockSparseSelfAdjointView * Dense: dimension mismatch");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(m_matrix, rhs, "BlockSparseSelfAdjointView * Dense: dimension mismatch");
     using ResultType = Matrix<Scalar, Dynamic, OtherDerived::ColsAtCompileTime>;
     ResultType result = ResultType::Zero(m_matrix.rows(), rhs.cols());
 
diff --git a/Eigen/src/SparseCore/SparseDot.h b/Eigen/src/SparseCore/SparseDot.h
index 77ff573..bcd4916 100644
--- a/Eigen/src/SparseCore/SparseDot.h
+++ b/Eigen/src/SparseCore/SparseDot.h
@@ -22,12 +22,11 @@
     const MatrixBase<OtherDerived>& other) const {
   EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
   EIGEN_STATIC_ASSERT_VECTOR_ONLY(OtherDerived)
-  EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(Derived, OtherDerived)
   EIGEN_STATIC_ASSERT(
       (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
       YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
 
-  eigen_assert(size() == other.size());
+  EIGEN_INTERNAL_ASSERT_SAME_SIZE(*this, other);
   if (size() == 0) return Scalar(0);
 
   internal::evaluator<Derived> thisEval(derived());
@@ -52,12 +51,11 @@
     const SparseMatrixBase<OtherDerived>& other) const {
   EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
   EIGEN_STATIC_ASSERT_VECTOR_ONLY(OtherDerived)
-  EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(Derived, OtherDerived)
   EIGEN_STATIC_ASSERT(
       (std::is_same<Scalar, typename OtherDerived::Scalar>::value),
       YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
 
-  eigen_assert(size() == other.size());
+  EIGEN_INTERNAL_ASSERT_SAME_SIZE(*this, other);
   if (size() == 0) return Scalar(0);
 
   internal::evaluator<Derived> thisEval(derived());
diff --git a/Eigen/src/SparseCore/SparseSelfAdjointView.h b/Eigen/src/SparseCore/SparseSelfAdjointView.h
index 2c756e4..f49089a 100644
--- a/Eigen/src/SparseCore/SparseSelfAdjointView.h
+++ b/Eigen/src/SparseCore/SparseSelfAdjointView.h
@@ -69,7 +69,7 @@
       SparseMatrix<Scalar, (MatrixTypeNested_::Flags & RowMajorBit) ? RowMajor : ColMajor, StorageIndex>;
 
   explicit inline SparseSelfAdjointView(MatrixType& matrix) : m_matrix(matrix) {
-    eigen_assert(rows() == cols() && "SelfAdjointView is only for squared matrices");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(matrix, "SelfAdjointView is only for squared matrices");
   }
 
   inline Index rows() const { return m_matrix.rows(); }
diff --git a/Eigen/src/SparseCore/SparseSolverBase.h b/Eigen/src/SparseCore/SparseSolverBase.h
index 10f4c63..d91477f 100644
--- a/Eigen/src/SparseCore/SparseSolverBase.h
+++ b/Eigen/src/SparseCore/SparseSolverBase.h
@@ -87,7 +87,8 @@
   template <typename Rhs>
   inline Solve<Derived, Rhs> solve(const MatrixBase<Rhs>& b) const {
     eigen_assert(m_isInitialized && "Solver is not initialized.");
-    eigen_assert(derived().rows() == b.rows() && "solve(): invalid number of rows of the right hand side matrix b");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(derived(), b, false,
+                                         "solve(): invalid number of rows of the right hand side matrix b");
     return Solve<Derived, Rhs>(derived(), b.derived());
   }
 
@@ -98,7 +99,8 @@
   template <typename Rhs>
   inline Solve<Derived, Rhs> solve(const SparseMatrixBase<Rhs>& b) const {
     eigen_assert(m_isInitialized && "Solver is not initialized.");
-    eigen_assert(derived().rows() == b.rows() && "solve(): invalid number of rows of the right hand side matrix b");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(derived(), b, false,
+                                         "solve(): invalid number of rows of the right hand side matrix b");
     return Solve<Derived, Rhs>(derived(), b.derived());
   }
 
diff --git a/Eigen/src/SparseCore/ThreadedSparseProduct.h b/Eigen/src/SparseCore/ThreadedSparseProduct.h
index db62f8a..7cadb51 100644
--- a/Eigen/src/SparseCore/ThreadedSparseProduct.h
+++ b/Eigen/src/SparseCore/ThreadedSparseProduct.h
@@ -234,13 +234,8 @@
   template <bool Adjoint, bool Overwrite>
   void apply_impl(const ConstVectorRef& x, MutableVectorRef& y, const Scalar& alpha) const {
     eigen_assert(m_mat && "ThreadedSparseProduct: matrix not set; call analyzePattern() first");
-    if (Adjoint) {
-      eigen_assert(x.size() == m_mat->rows());
-      eigen_assert(y.size() == m_mat->cols());
-    } else {
-      eigen_assert(x.size() == m_mat->cols());
-      eigen_assert(y.size() == m_mat->rows());
-    }
+    EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS(x, *m_mat, Adjoint);
+    EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_AXIS(y, *m_mat, !Adjoint);
     // The kernel reads x and writes y concurrently across threads; aliasing
     // (x and y overlap) would mis-compute because some x[k] reads see y
     // writes from the same SpMV call. Cheap address-range check; cast to
diff --git a/Eigen/src/SparseCore/TriangularSolver.h b/Eigen/src/SparseCore/TriangularSolver.h
index 0f306e9..588e6b7 100644
--- a/Eigen/src/SparseCore/TriangularSolver.h
+++ b/Eigen/src/SparseCore/TriangularSolver.h
@@ -155,7 +155,8 @@
 template <typename ExpressionType, unsigned int Mode>
 template <typename OtherDerived>
 void TriangularViewImpl<ExpressionType, Mode, Sparse>::solveInPlace(MatrixBase<OtherDerived>& other) const {
-  eigen_assert(derived().cols() == derived().rows() && derived().cols() == other.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(derived());
+  EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(derived(), other);
   eigen_assert((!(Mode & ZeroDiag)) && bool(Mode & (Upper | Lower)));
 
   enum { copy = internal::traits<OtherDerived>::Flags & RowMajorBit };
@@ -310,7 +311,8 @@
 template <typename ExpressionType, unsigned int Mode>
 template <typename OtherDerived>
 void TriangularViewImpl<ExpressionType, Mode, Sparse>::solveInPlace(SparseMatrixBase<OtherDerived>& other) const {
-  eigen_assert(derived().cols() == derived().rows() && derived().cols() == other.rows());
+  EIGEN_INTERNAL_ASSERT_SQUARE(derived());
+  EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(derived(), other);
   eigen_assert((!(Mode & ZeroDiag)) && bool(Mode & (Upper | Lower)));
 
   internal::sparse_solve_triangular_sparse_selector<ExpressionType, OtherDerived, Mode>::run(
diff --git a/Eigen/src/SparseLU/SparseLU.h b/Eigen/src/SparseLU/SparseLU.h
index f0db58d..7316cf6 100644
--- a/Eigen/src/SparseLU/SparseLU.h
+++ b/Eigen/src/SparseLU/SparseLU.h
@@ -613,7 +613,7 @@
 void SparseLU<MatrixType, OrderingType>::factorize(const MatrixType& matrix) {
   using internal::emptyIdxLU;
   eigen_assert(m_analysisIsOk && "analyzePattern() should be called first");
-  eigen_assert((matrix.rows() == matrix.cols()) && "Only for squared matrices");
+  EIGEN_INTERNAL_ASSERT_SQUARE_MSG(matrix, "Only for squared matrices");
 
   m_isInitialized = true;
 
diff --git a/Eigen/src/SparseQR/SparseQR.h b/Eigen/src/SparseQR/SparseQR.h
index e316270..9940138 100644
--- a/Eigen/src/SparseQR/SparseQR.h
+++ b/Eigen/src/SparseQR/SparseQR.h
@@ -231,8 +231,8 @@
   template <typename Rhs, typename Dest>
   bool _solve_impl(const MatrixBase<Rhs>& B, MatrixBase<Dest>& dest) const {
     eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
-    eigen_assert(this->rows() == B.rows() &&
-                 "SparseQR::solve() : invalid number of rows in the right hand side matrix");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(*this, B, false,
+                                         "SparseQR::solve() : invalid number of rows in the right hand side matrix");
 
     Index rank = this->rank();
 
@@ -275,15 +275,15 @@
   template <typename Rhs>
   inline Solve<SparseQR, Rhs> solve(const MatrixBase<Rhs>& B) const {
     eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
-    eigen_assert(this->rows() == B.rows() &&
-                 "SparseQR::solve() : invalid number of rows in the right hand side matrix");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(*this, B, false,
+                                         "SparseQR::solve() : invalid number of rows in the right hand side matrix");
     return Solve<SparseQR, Rhs>(*this, B.derived());
   }
   template <typename Rhs>
   inline Solve<SparseQR, Rhs> solve(const SparseMatrixBase<Rhs>& B) const {
     eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
-    eigen_assert(this->rows() == B.rows() &&
-                 "SparseQR::solve() : invalid number of rows in the right hand side matrix");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(*this, B, false,
+                                         "SparseQR::solve() : invalid number of rows in the right hand side matrix");
     return Solve<SparseQR, Rhs>(*this, B.derived());
   }
 
@@ -735,7 +735,7 @@
     Index diagSize = (std::min)(m, n);
     res = m_other;
     if (m_transpose) {
-      eigen_assert(m_qr.m_Q.rows() == m_other.rows() && "Non conforming object sizes");
+      EIGEN_INTERNAL_ASSERT_SAME_ROWS_MSG(m_qr.m_Q, m_other, "Non conforming object sizes");
       // Compute res = Q' * other column by column
       for (Index j = 0; j < res.cols(); j++) {
         for (Index k = 0; k < diagSize; k++) {
@@ -747,7 +747,7 @@
         }
       }
     } else {
-      eigen_assert(m_qr.matrixQ().cols() == m_other.rows() && "Non conforming object sizes");
+      EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(m_qr.matrixQ(), m_other, "Non conforming object sizes");
 
       res.conservativeResize(rows(), cols());
 
diff --git a/cmake/EigenTesting.cmake b/cmake/EigenTesting.cmake
index 43d5fdf..ae21d7e 100644
--- a/cmake/EigenTesting.cmake
+++ b/cmake/EigenTesting.cmake
@@ -273,6 +273,8 @@
 
   add_executable(${test_target_ok} ${testname}.cpp)
   add_executable(${test_target_ko} ${testname}.cpp)
+  target_link_libraries(${test_target_ok} PRIVATE Eigen3::Eigen)
+  target_link_libraries(${test_target_ko} PRIVATE Eigen3::Eigen)
   set_target_properties(${test_target_ok} ${test_target_ko} PROPERTIES
                         EXCLUDE_FROM_ALL TRUE
                         EXCLUDE_FROM_DEFAULT_BUILD TRUE)
diff --git a/contrib/Eigen/FFT b/contrib/Eigen/FFT
index 56addd9..c754a52 100644
--- a/contrib/Eigen/FFT
+++ b/contrib/Eigen/FFT
@@ -308,7 +308,7 @@
         YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
     EIGEN_STATIC_ASSERT(int(InputDerived::Flags) & int(ComplexDerived::Flags) & DirectAccessBit,
                         THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES)
-    eigen_assert((src.rows() == 1 || src.cols() == 1) && "the input of an FFT must be one-dimensional");
+    EIGEN_INTERNAL_ASSERT_VECTOR(src);
 
     if (nfft < 1) nfft = src.size();
 
@@ -381,7 +381,7 @@
         YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
     EIGEN_STATIC_ASSERT(int(OutputDerived::Flags) & int(ComplexDerived::Flags) & DirectAccessBit,
                         THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES)
-    eigen_assert((src.rows() == 1 || src.cols() == 1) && "the input of an FFT must be one-dimensional");
+    EIGEN_INTERNAL_ASSERT_VECTOR(src);
 
     if (nfft < 1) {  // automatic FFT size determination
       if (realfft && HasFlag(HalfSpectrum))
diff --git a/contrib/Eigen/src/MatrixFunctions/MatrixExponential.h b/contrib/Eigen/src/MatrixFunctions/MatrixExponential.h
index 14e2b95..0bf8205 100644
--- a/contrib/Eigen/src/MatrixFunctions/MatrixExponential.h
+++ b/contrib/Eigen/src/MatrixFunctions/MatrixExponential.h
@@ -423,7 +423,7 @@
 
 template <typename Derived>
 const MatrixExponentialReturnValue<Derived> MatrixBase<Derived>::exp() const {
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   return MatrixExponentialReturnValue<Derived>(derived());
 }
 
diff --git a/contrib/Eigen/src/MatrixFunctions/MatrixFunction.h b/contrib/Eigen/src/MatrixFunctions/MatrixFunction.h
index a348da0..917847d 100644
--- a/contrib/Eigen/src/MatrixFunctions/MatrixFunction.h
+++ b/contrib/Eigen/src/MatrixFunctions/MatrixFunction.h
@@ -266,12 +266,12 @@
  */
 template <typename MatrixType>
 MatrixType matrix_function_solve_triangular_sylvester(const MatrixType& A, const MatrixType& B, const MatrixType& C) {
-  eigen_assert(A.rows() == A.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(A);
   eigen_assert(A.isUpperTriangular());
-  eigen_assert(B.rows() == B.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(B);
   eigen_assert(B.isUpperTriangular());
-  eigen_assert(C.rows() == A.rows());
-  eigen_assert(C.cols() == B.rows());
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(C, A);
+  EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(C, B);
 
   typedef typename MatrixType::Scalar Scalar;
 
@@ -510,34 +510,34 @@
 template <typename Derived>
 const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::matrixFunction(
     typename internal::stem_function<typename internal::traits<Derived>::Scalar>::type f) const {
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   return MatrixFunctionReturnValue<Derived>(derived(), f);
 }
 
 template <typename Derived>
 const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sin() const {
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
   return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sin<ComplexScalar>);
 }
 
 template <typename Derived>
 const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cos() const {
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
   return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cos<ComplexScalar>);
 }
 
 template <typename Derived>
 const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sinh() const {
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
   return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sinh<ComplexScalar>);
 }
 
 template <typename Derived>
 const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cosh() const {
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
   return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cosh<ComplexScalar>);
 }
diff --git a/contrib/Eigen/src/MatrixFunctions/MatrixLogarithm.h b/contrib/Eigen/src/MatrixFunctions/MatrixLogarithm.h
index b169ba6..d285b0e 100644
--- a/contrib/Eigen/src/MatrixFunctions/MatrixLogarithm.h
+++ b/contrib/Eigen/src/MatrixFunctions/MatrixLogarithm.h
@@ -373,7 +373,7 @@
 
 template <typename Derived>
 const MatrixLogarithmReturnValue<Derived> MatrixBase<Derived>::log() const {
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   return MatrixLogarithmReturnValue<Derived>(derived());
 }
 
diff --git a/contrib/Eigen/src/MatrixFunctions/MatrixPower.h b/contrib/Eigen/src/MatrixFunctions/MatrixPower.h
index d090d95..293a8ac 100644
--- a/contrib/Eigen/src/MatrixFunctions/MatrixPower.h
+++ b/contrib/Eigen/src/MatrixFunctions/MatrixPower.h
@@ -135,7 +135,7 @@
 
 template <typename MatrixType>
 MatrixPowerAtomic<MatrixType>::MatrixPowerAtomic(const MatrixType& T, RealScalar p) : m_A(T), m_p(p) {
-  eigen_assert(T.rows() == T.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(T);
   eigen_assert(p > -1 && p < 1);
 }
 
@@ -369,7 +369,7 @@
    * (or destroyed) before evaluation.
    */
   explicit MatrixPower(const MatrixType& A) : m_A(A), m_conditionNumber(0), m_rank(A.cols()), m_nulls(0) {
-    eigen_assert(A.rows() == A.cols());
+    EIGEN_INTERNAL_ASSERT_SQUARE(A);
   }
 
   /**
diff --git a/contrib/Eigen/src/MatrixFunctions/MatrixSquareRoot.h b/contrib/Eigen/src/MatrixFunctions/MatrixSquareRoot.h
index 4aac6f5..37a8831 100644
--- a/contrib/Eigen/src/MatrixFunctions/MatrixSquareRoot.h
+++ b/contrib/Eigen/src/MatrixFunctions/MatrixSquareRoot.h
@@ -170,7 +170,7 @@
  */
 template <typename MatrixType, typename ResultType>
 void matrix_sqrt_quasi_triangular(const MatrixType& arg, ResultType& result) {
-  eigen_assert(arg.rows() == arg.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(arg);
   result.resize(arg.rows(), arg.cols());
   internal::matrix_sqrt_quasi_triangular_diagonal(arg, result);
   internal::matrix_sqrt_quasi_triangular_off_diagonal(arg, result);
@@ -195,7 +195,7 @@
   using std::sqrt;
   typedef typename MatrixType::Scalar Scalar;
 
-  eigen_assert(arg.rows() == arg.cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(arg);
 
   // Compute square root of arg and store it in upper triangular part of result
   // This uses that the square root of triangular matrices can be computed directly.
@@ -242,7 +242,7 @@
   typedef typename MatrixType::PlainObject PlainType;
   template <typename ResultType>
   static void run(const MatrixType& arg, ResultType& result) {
-    eigen_assert(arg.rows() == arg.cols());
+    EIGEN_INTERNAL_ASSERT_SQUARE(arg);
 
     // Compute Schur decomposition of arg
     const RealSchur<PlainType> schurOfA(arg);
@@ -265,7 +265,7 @@
   typedef typename MatrixType::PlainObject PlainType;
   template <typename ResultType>
   static void run(const MatrixType& arg, ResultType& result) {
-    eigen_assert(arg.rows() == arg.cols());
+    EIGEN_INTERNAL_ASSERT_SQUARE(arg);
 
     // Compute Schur decomposition of arg
     const ComplexSchur<PlainType> schurOfA(arg);
@@ -337,7 +337,7 @@
 
 template <typename Derived>
 const MatrixSquareRootReturnValue<Derived> MatrixBase<Derived>::sqrt() const {
-  eigen_assert(rows() == cols());
+  EIGEN_INTERNAL_ASSERT_SQUARE(*this);
   return MatrixSquareRootReturnValue<Derived>(derived());
 }
 
diff --git a/contrib/Eigen/src/StructuredMatrices/Bccb.h b/contrib/Eigen/src/StructuredMatrices/Bccb.h
index 655c036..a95eb31 100644
--- a/contrib/Eigen/src/StructuredMatrices/Bccb.h
+++ b/contrib/Eigen/src/StructuredMatrices/Bccb.h
@@ -165,7 +165,7 @@
    * cached transform. */
   template <typename Derived>
   explicit Bccb(const MatrixBase<Derived>& generator) : m_g(generator) {
-    eigen_assert(m_g.rows() > 0 && m_g.cols() > 0 && "Bccb generator must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_g, "Bccb generator must be non-empty");
     if (m_g.size() > internal::structured_direct_threshold()) {
       m_symbol = computeSymbol();
       m_prodSymbol = computeProdSymbol(m_g);
@@ -228,11 +228,8 @@
    * multiple right-hand sides. */
   template <typename Rhs>
   Matrix<Scalar, RowsAtCompileTime, Rhs::ColsAtCompileTime> solve(const MatrixBase<Rhs>& b) const {
-    EIGEN_STATIC_ASSERT(RowsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(RowsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(*this, b, false, "right-hand side has the wrong number of rows");
     const Index N = rows();
-    eigen_assert(b.rows() == N && "right-hand side has the wrong number of rows");
     const ComplexArray s = symbol();
     const RealArray mods = s.cwiseAbs();
     const ComplexArray sinv = internal::structured_pinv_symbol(s, mods, internal::structured_rank_threshold(s, mods));
@@ -370,10 +367,6 @@
    * it. */
   template <typename Rhs>
   Product<Bccb, Rhs> operator*(const MatrixBase<Rhs>& x) const {
-    EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        INVALID_MATRIX_PRODUCT)
-    eigen_assert(x.rows() == cols() && "invalid product: dimensions do not match");
     return Product<Bccb, Rhs>(*this, x.derived());
   }
 
@@ -383,7 +376,7 @@
   template <typename Dest, typename Rhs, typename ProductScalar>
   void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
     const Index N = rows();
-    eigen_assert(rhs.rows() == N && "invalid product: dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(*this, rhs, "invalid product: dimensions do not match");
     if (N <= internal::structured_direct_threshold()) {
       directProduct(dst, rhs, alpha);
       return;
diff --git a/contrib/Eigen/src/StructuredMatrices/Cauchy.h b/contrib/Eigen/src/StructuredMatrices/Cauchy.h
index 99e52b2..f6f5019 100644
--- a/contrib/Eigen/src/StructuredMatrices/Cauchy.h
+++ b/contrib/Eigen/src/StructuredMatrices/Cauchy.h
@@ -134,7 +134,8 @@
   Cauchy(const MatrixBase<XDerived>& x, const MatrixBase<YDerived>& y) : m_x(x), m_y(y) {
     EIGEN_STATIC_ASSERT_VECTOR_ONLY(XDerived)
     EIGEN_STATIC_ASSERT_VECTOR_ONLY(YDerived)
-    eigen_assert(m_x.size() > 0 && m_y.size() > 0 && "Cauchy node vectors must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_x, "Cauchy node vectors must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_y, "Cauchy node vectors must be non-empty");
   }
 
   EIGEN_DEVICE_FUNC Index rows() const { return m_x.size(); }
@@ -175,7 +176,7 @@
    * a node difference that overflows makes its factor infinite, which turns the
    * determinant into NaN when it meets a zero or another infinite factor. */
   Scalar determinant() const {
-    eigen_assert(rows() == cols() && "Cauchy::determinant requires a square matrix");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(*this, "Cauchy::determinant requires a square matrix");
     const Index n = rows();
     Scalar det(1);
     Index exponent = 0;
@@ -222,10 +223,6 @@
    * \a v, and \c .noalias() skips it. */
   template <typename Rhs>
   Product<Cauchy, Rhs> operator*(const MatrixBase<Rhs>& v) const {
-    EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        INVALID_MATRIX_PRODUCT)
-    eigen_assert(v.rows() == cols() && "invalid product: dimensions do not match");
     return Product<Cauchy, Rhs>(*this, v.derived());
   }
 
@@ -238,7 +235,7 @@
   template <typename Dest, typename Rhs, typename ProductScalar>
   void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
     const Index n = cols();
-    eigen_assert(rhs.rows() == n && "invalid product: dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(*this, rhs, "invalid product: dimensions do not match");
     // A unit alpha must not multiply: even the identity complex scalar (1,0)
     // pollutes an (Inf,0) value with NaN through the 0*Inf cross term.
     const bool unitAlpha = alpha == ProductScalar(1);
@@ -351,7 +348,7 @@
    * \f$D_x C-C D_y=\mathbf{1}\mathbf{1}^T\f$. \sa solve */
   template <int Rows_, int Cols_>
   CauchyLU& compute(const Cauchy<Scalar, Rows_, Cols_>& C) {
-    eigen_assert(C.rows() == C.cols() && "CauchyLU requires a square Cauchy matrix");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(C, "CauchyLU requires a square Cauchy matrix");
     const Index n = C.rows();
     DenseVector x = C.rowNodes();
     const DenseVector y = C.colNodes();
diff --git a/contrib/Eigen/src/StructuredMatrices/Circulant.h b/contrib/Eigen/src/StructuredMatrices/Circulant.h
index bb1ebc5..e078899 100644
--- a/contrib/Eigen/src/StructuredMatrices/Circulant.h
+++ b/contrib/Eigen/src/StructuredMatrices/Circulant.h
@@ -125,7 +125,7 @@
   template <typename Derived>
   explicit Circulant(const MatrixBase<Derived>& col) : m_col(col) {
     EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
-    eigen_assert(m_col.size() > 0 && "Circulant generator must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_col, "Circulant generator must be non-empty");
     if (m_col.size() > internal::structured_direct_threshold()) {
       m_symbol = computeSymbol();
       m_prodSymbol = computeProdSymbol(m_col);
@@ -243,10 +243,6 @@
    * aliasing between the destination and \a x, and \c .noalias() skips it. */
   template <typename Rhs>
   Product<Circulant, Rhs> operator*(const MatrixBase<Rhs>& x) const {
-    EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        INVALID_MATRIX_PRODUCT)
-    eigen_assert(x.rows() == cols() && "invalid product: dimensions do not match");
     return Product<Circulant, Rhs>(*this, x.derived());
   }
 
@@ -258,11 +254,8 @@
    * \c x = ifft( fft(b) ./ fft(c) ). Supports multiple right-hand sides. */
   template <typename Rhs>
   Matrix<Scalar, Size_, Rhs::ColsAtCompileTime> solve(const MatrixBase<Rhs>& b) const {
-    EIGEN_STATIC_ASSERT(RowsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(RowsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(*this, b, false, "right-hand side has the wrong number of rows");
     const Index n = rows();
-    eigen_assert(b.rows() == n && "right-hand side has the wrong number of rows");
     const ComplexVector s = symbol();
     const RealVector mods = s.cwiseAbs();
     const ComplexVector sinv = internal::structured_pinv_symbol(s, mods, internal::structured_rank_threshold(s, mods));
@@ -391,7 +384,7 @@
   template <typename Dest, typename Rhs, typename ProductScalar>
   void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
     const Index n = rows();
-    eigen_assert(rhs.rows() == n && "invalid product: dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(*this, rhs, "invalid product: dimensions do not match");
     if (n <= internal::structured_direct_threshold()) {
       directProduct(dst, rhs, alpha);
       return;
diff --git a/contrib/Eigen/src/StructuredMatrices/DPR1EigenSolver.h b/contrib/Eigen/src/StructuredMatrices/DPR1EigenSolver.h
index 867dc9d..daa1b43 100644
--- a/contrib/Eigen/src/StructuredMatrices/DPR1EigenSolver.h
+++ b/contrib/Eigen/src/StructuredMatrices/DPR1EigenSolver.h
@@ -143,7 +143,7 @@
 DPR1EigenSolver<RealScalar_>& DPR1EigenSolver<RealScalar_>::compute(const VectorType& d, RealScalar rho,
                                                                     const VectorType& z, int options) {
   const Index n = d.size();
-  eigen_assert(z.size() == n && "d and z must have the same size");
+  EIGEN_INTERNAL_ASSERT_SAME_SIZE_MSG(d, z, "d and z must have the same size");
   eigen_assert((options & ~EigVecMask) == 0 && (options & EigVecMask) != EigVecMask && "invalid option parameter");
   const bool computeVectors = (options & ComputeEigenvectors) == ComputeEigenvectors;
   m_vectorsComputed = false;
diff --git a/contrib/Eigen/src/StructuredMatrices/DiagonalPlusLowRank.h b/contrib/Eigen/src/StructuredMatrices/DiagonalPlusLowRank.h
index 9dd76f7..c8b45d3 100644
--- a/contrib/Eigen/src/StructuredMatrices/DiagonalPlusLowRank.h
+++ b/contrib/Eigen/src/StructuredMatrices/DiagonalPlusLowRank.h
@@ -173,9 +173,10 @@
   DiagonalPlusLowRank(const MatrixBase<DDerived>& d, const MatrixBase<UDerived>& U, const MatrixBase<VDerived>& V)
       : m_d(d), m_U(U), m_V(V) {
     EIGEN_STATIC_ASSERT_VECTOR_ONLY(DDerived)
-    eigen_assert(m_d.size() > 0 && "DiagonalPlusLowRank must be non-empty");
-    eigen_assert(m_U.rows() == m_d.size() && m_V.rows() == m_d.size() && m_U.cols() == m_V.cols() &&
-                 "factor dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_d, "DiagonalPlusLowRank must be non-empty");
+    EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS_MSG(m_d, m_U, "factor dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS_MSG(m_d, m_V, "factor dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_SAME_COLS_MSG(m_U, m_V, "factor dimensions do not match");
   }
 
   EIGEN_DEVICE_FUNC Index rows() const { return m_d.size(); }
@@ -238,10 +239,7 @@
    * accuracy degrades accordingly, even for a well-conditioned operator [5]. */
   template <typename Rhs>
   Matrix<Scalar, Size_, Rhs::ColsAtCompileTime> solve(const MatrixBase<Rhs>& b) const {
-    EIGEN_STATIC_ASSERT(RowsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(RowsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
-    eigen_assert(b.rows() == rows() && "right-hand side has the wrong number of rows");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(*this, b, false, "right-hand side has the wrong number of rows");
     const DiagonalVector dinv = m_d.cwiseInverse();
     Matrix<Scalar, Size_, Rhs::ColsAtCompileTime> x = dinv.asDiagonal() * b;
     internal::dplr_capacitance_impl<Rank_>::subtractSolveCorrection(*this, dinv, x);
@@ -318,10 +316,6 @@
    * it. */
   template <typename Rhs>
   Product<DiagonalPlusLowRank, Rhs> operator*(const MatrixBase<Rhs>& v) const {
-    EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        INVALID_MATRIX_PRODUCT)
-    eigen_assert(v.rows() == cols() && "invalid product: dimensions do not match");
     return Product<DiagonalPlusLowRank, Rhs>(*this, v.derived());
   }
 
@@ -331,7 +325,7 @@
    * promoted type. */
   template <typename Dest, typename Rhs, typename ProductScalar>
   void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
-    eigen_assert(rhs.rows() == rows() && "invalid product: dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(*this, rhs, "invalid product: dimensions do not match");
     const Matrix<ProductScalar, Size_, Rhs::ColsAtCompileTime> r = rhs;
     dst += alpha * (m_d.asDiagonal() * r);
     if (correctionRank() > 0) {
diff --git a/contrib/Eigen/src/StructuredMatrices/Hankel.h b/contrib/Eigen/src/StructuredMatrices/Hankel.h
index 65efd15..38a8b2f 100644
--- a/contrib/Eigen/src/StructuredMatrices/Hankel.h
+++ b/contrib/Eigen/src/StructuredMatrices/Hankel.h
@@ -134,7 +134,8 @@
     EIGEN_STATIC_ASSERT_VECTOR_ONLY(ColDerived)
     EIGEN_STATIC_ASSERT_VECTOR_ONLY(RowDerived)
     const Index m = col.size(), n = row.size();
-    eigen_assert(m > 0 && n > 0 && "Hankel generators must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_SIZE_MSG(col, m, "Hankel generators must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_SIZE_MSG(row, n, "Hankel generators must be non-empty");
     m_h.resize(m + n - 1);
     m_h.head(m) = col;
     if (n > 1) m_h.tail(n - 1) = row.tail(n - 1);
@@ -210,11 +211,8 @@
    * the solution rows. */
   template <typename Rhs>
   Matrix<Scalar, SolveRowsAtCompileTime, Rhs::ColsAtCompileTime> solve(const MatrixBase<Rhs>& b) const {
-    EIGEN_STATIC_ASSERT(RowsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(RowsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
-    eigen_assert(rows() == cols() && "Hankel::solve requires a square matrix");
-    eigen_assert(b.rows() == rows() && "right-hand side has the wrong number of rows");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(*this, "Hankel::solve requires a square matrix");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(*this, b, false, "right-hand side has the wrong number of rows");
     LookAheadLevinson<Scalar> levinson(toToeplitz());
     eigen_assert(levinson.info() == Success &&
                  "Hankel::solve: the Levinson recursion broke down; "
@@ -260,10 +258,6 @@
    * aliasing between the destination and \a x, and \c .noalias() skips it. */
   template <typename Rhs>
   Product<Hankel, Rhs> operator*(const MatrixBase<Rhs>& x) const {
-    EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        INVALID_MATRIX_PRODUCT)
-    eigen_assert(x.rows() == cols() && "invalid product: dimensions do not match");
     return Product<Hankel, Rhs>(*this, x.derived());
   }
 
@@ -278,7 +272,7 @@
   template <typename Dest, typename Rhs, typename ProductScalar>
   void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
     const Index m = rows(), n = cols();
-    eigen_assert(rhs.rows() == n && "invalid product: dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(*this, rhs, "invalid product: dimensions do not match");
     const bool small = m == 1 || n == 1 ||
                        (m <= internal::structured_direct_threshold() && n <= internal::structured_direct_threshold());
     if (small) {
diff --git a/contrib/Eigen/src/StructuredMatrices/KroneckerOperator.h b/contrib/Eigen/src/StructuredMatrices/KroneckerOperator.h
index 4e83bda..1a937e7 100644
--- a/contrib/Eigen/src/StructuredMatrices/KroneckerOperator.h
+++ b/contrib/Eigen/src/StructuredMatrices/KroneckerOperator.h
@@ -968,7 +968,8 @@
   template <typename LhsDerived, typename RhsDerived>
   KroneckerOperator(const EigenBase<LhsDerived>& a, const EigenBase<RhsDerived>& b)
       : m_A(a.derived()), m_B(b.derived()) {
-    eigen_assert(m_A.size() > 0 && m_B.size() > 0 && "KroneckerOperator factors must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_A, "KroneckerOperator factors must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_B, "KroneckerOperator factors must be non-empty");
     LhsOps::prepare(m_A);
     RhsOps::prepare(m_B);
   }
@@ -1032,12 +1033,10 @@
    * \ref leastSquaresSolve for rank-deficient or rectangular factors. */
   template <typename Rhs>
   Matrix<Scalar, ColsAtCompileTime, Rhs::ColsAtCompileTime> solve(const MatrixBase<Rhs>& b) const {
-    EIGEN_STATIC_ASSERT(RowsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(RowsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
     const Index n1 = m_A.cols(), n2 = m_B.cols();
-    eigen_assert(m_A.rows() == n1 && m_B.rows() == n2 && "KroneckerOperator::solve requires square factors");
-    eigen_assert(b.rows() == n1 * n2 && "right-hand side has the wrong number of rows");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_A, "KroneckerOperator::solve requires square factors");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_B, "KroneckerOperator::solve requires square factors");
+    EIGEN_INTERNAL_ASSERT_SOLVER_RHS_MSG(*this, b, false, "right-hand side has the wrong number of rows");
     const internal::kron_factor_solver<LhsMatrix> solverA(m_A);
     const internal::kron_factor_solver<RhsMatrix> solverB(m_B);
     Matrix<Scalar, ColsAtCompileTime, Rhs::ColsAtCompileTime> x(n1 * n2, b.cols());
@@ -1139,8 +1138,8 @@
    * matrix, computed by one \c SparseLU solve against the identity (NaN when
    * the factorization fails). */
   KroneckerOperator<typename LhsOps::InverseFactor, typename RhsOps::InverseFactor> inverse() const {
-    eigen_assert(m_A.rows() == m_A.cols() && m_B.rows() == m_B.cols() &&
-                 "KroneckerOperator::inverse requires square factors");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_A, "KroneckerOperator::inverse requires square factors");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_B, "KroneckerOperator::inverse requires square factors");
     return {LhsOps::inversed(m_A), RhsOps::inversed(m_B)};
   }
 
@@ -1157,8 +1156,8 @@
    * underflow on their own) never leave the representable range when the
    * determinant itself is representable. */
   Scalar determinant() const {
-    eigen_assert(m_A.rows() == m_A.cols() && m_B.rows() == m_B.cols() &&
-                 "KroneckerOperator::determinant requires square factors");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_A, "KroneckerOperator::determinant requires square factors");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_B, "KroneckerOperator::determinant requires square factors");
     Index exponent = 0;
     const Scalar mant = balancedDeterminant(exponent);
     return internal::structured_ldexp_clamped(mant, exponent);
@@ -1173,8 +1172,8 @@
    * product must then be square -- with the accuracy
    * KroneckerSum::eigenvalues() describes for a sum. */
   ComplexVector eigenvalues() const {
-    eigen_assert(m_A.rows() == m_A.cols() && m_B.rows() == m_B.cols() &&
-                 "KroneckerOperator::eigenvalues requires square factors");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_A, "KroneckerOperator::eigenvalues requires square factors");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_B, "KroneckerOperator::eigenvalues requires square factors");
     // Column-major stacking of the nB x nA outer product puts mu_j(B) lambda_i(A)
     // at index i*nB + j, the Kronecker order.
     return (RhsSpectrum::eigenvalues(m_B) * LhsSpectrum::eigenvalues(m_A).transpose()).reshaped();
@@ -1185,8 +1184,8 @@
    * \c i*n2 + j is \f$ v_i(A) \otimes v_j(B) \f$ and matches
    * \c eigenvalues()[i*n2 + j]. Assign it to a dense matrix to materialize. */
   KroneckerOperator<typename LhsSpectrum::Eigenvectors, typename RhsSpectrum::Eigenvectors> eigenvectors() const {
-    eigen_assert(m_A.rows() == m_A.cols() && m_B.rows() == m_B.cols() &&
-                 "KroneckerOperator::eigenvectors requires square factors");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_A, "KroneckerOperator::eigenvectors requires square factors");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(m_B, "KroneckerOperator::eigenvectors requires square factors");
     return {LhsSpectrum::eigenvectors(m_A), RhsSpectrum::eigenvectors(m_B)};
   }
 
@@ -1327,10 +1326,6 @@
    * \c .noalias() skips it. */
   template <typename Rhs>
   Product<KroneckerOperator, Rhs> operator*(const MatrixBase<Rhs>& x) const {
-    EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        INVALID_MATRIX_PRODUCT)
-    eigen_assert(x.rows() == cols() && "invalid product: dimensions do not match");
     return Product<KroneckerOperator, Rhs>(*this, x.derived());
   }
 
@@ -1356,7 +1351,7 @@
   void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
     using ProductMatrix = Matrix<ProductScalar, Dynamic, Dynamic, ColMajor>;  // ColMajor: see DenseMatrix
     const Index m1 = m_A.rows(), n1 = m_A.cols(), m2 = m_B.rows(), n2 = m_B.cols(), r = rhs.cols();
-    eigen_assert(rhs.rows() == n1 * n2 && "invalid product: dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(*this, rhs, "invalid product: dimensions do not match");
     typename internal::nested_eval<Rhs, 1>::type actualRhs(rhs);
     const bool skipA = LhsOps::isSquareIdentity(m_A), skipB = RhsOps::isSquareIdentity(m_B);
     ProductMatrix X, BX, Y, work;
diff --git a/contrib/Eigen/src/StructuredMatrices/LookAheadLevinson.h b/contrib/Eigen/src/StructuredMatrices/LookAheadLevinson.h
index b4f8c81..f37c4c8 100644
--- a/contrib/Eigen/src/StructuredMatrices/LookAheadLevinson.h
+++ b/contrib/Eigen/src/StructuredMatrices/LookAheadLevinson.h
@@ -237,8 +237,9 @@
   using internal::structured_tdot;
   using internal::structured_upshift;
 
-  EIGEN_STATIC_ASSERT(Rows_ == Dynamic || Cols_ == Dynamic || Rows_ == Cols_, YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
-  eigen_assert(T.rows() == T.cols() && "LookAheadLevinson requires a square Toeplitz matrix");
+  EIGEN_INTERNAL_CHECK_SHAPE(T.rows() == T.cols() && "LookAheadLevinson requires a square Toeplitz matrix",
+                             YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES,
+                             internal::square_dimensions<Toeplitz<Scalar, Rows_, Cols_>>);
   const Index n = T.rows();
   const DenseVector c = T.column();  // (rho_0, sigma_1, ..., sigma_{n-1})
   const DenseVector r = T.row();     // (rho_0, rho_1,   ..., rho_{n-1})
diff --git a/contrib/Eigen/src/StructuredMatrices/Toeplitz.h b/contrib/Eigen/src/StructuredMatrices/Toeplitz.h
index bea571d..235912d 100644
--- a/contrib/Eigen/src/StructuredMatrices/Toeplitz.h
+++ b/contrib/Eigen/src/StructuredMatrices/Toeplitz.h
@@ -112,7 +112,8 @@
   Toeplitz(const MatrixBase<ColDerived>& col, const MatrixBase<RowDerived>& row) : m_col(col), m_row(row) {
     EIGEN_STATIC_ASSERT_VECTOR_ONLY(ColDerived)
     EIGEN_STATIC_ASSERT_VECTOR_ONLY(RowDerived)
-    eigen_assert(m_col.size() > 0 && m_row.size() > 0 && "Toeplitz generators must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_col, "Toeplitz generators must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(m_row, "Toeplitz generators must be non-empty");
     // Below the threshold every product takes the direct path and the symbol has
     // no consumer, so skip the embedding altogether.
     if (rows() > internal::structured_direct_threshold() || cols() > internal::structured_direct_threshold())
@@ -239,10 +240,6 @@
    * \c .noalias() skips it. */
   template <typename Rhs>
   Product<Toeplitz, Rhs> operator*(const MatrixBase<Rhs>& x) const {
-    EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        INVALID_MATRIX_PRODUCT)
-    eigen_assert(x.rows() == cols() && "invalid product: dimensions do not match");
     return Product<Toeplitz, Rhs>(*this, x.derived());
   }
 
@@ -252,7 +249,7 @@
   template <typename Dest, typename Rhs, typename ProductScalar>
   void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
     const Index m = rows(), n = cols();
-    eigen_assert(rhs.rows() == n && "invalid product: dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(*this, rhs, "invalid product: dimensions do not match");
     const bool small = m <= internal::structured_direct_threshold() && n <= internal::structured_direct_threshold();
     if (small)
       directProduct(dst, rhs, alpha);
diff --git a/contrib/Eigen/src/StructuredMatrices/Vandermonde.h b/contrib/Eigen/src/StructuredMatrices/Vandermonde.h
index 6a56aa0..40d011c 100644
--- a/contrib/Eigen/src/StructuredMatrices/Vandermonde.h
+++ b/contrib/Eigen/src/StructuredMatrices/Vandermonde.h
@@ -185,7 +185,7 @@
   template <typename Derived>
   Vandermonde(const MatrixBase<Derived>& nodes, Index cols) : m_x(nodes), m_cols(cols) {
     EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
-    eigen_assert(m_x.size() > 0 && m_cols > 0 && "Vandermonde must be non-empty");
+    EIGEN_INTERNAL_ASSERT_NONEMPTY_MSG(*this, "Vandermonde must be non-empty");
     eigen_assert((Cols_ == Dynamic || Cols_ == cols) && "cols does not match the compile-time column count");
   }
 
@@ -224,7 +224,7 @@
    * exactly; a node difference that overflows makes the determinant infinite, or
    * NaN when a zero factor is also present. */
   Scalar determinant() const {
-    eigen_assert(rows() == cols() && "Vandermonde::determinant requires a square matrix");
+    EIGEN_INTERNAL_ASSERT_SQUARE_MSG(*this, "Vandermonde::determinant requires a square matrix");
     const Index n = rows();
     Scalar det(1);
     internal::structured_exponent_type exponent = 0;
@@ -276,10 +276,6 @@
    * \c .noalias() skips it. */
   template <typename Rhs>
   Product<Vandermonde, Rhs> operator*(const MatrixBase<Rhs>& a) const {
-    EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
-                            int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
-                        INVALID_MATRIX_PRODUCT)
-    eigen_assert(a.rows() == cols() && "invalid product: dimensions do not match");
     return Product<Vandermonde, Rhs>(*this, a.derived());
   }
 
@@ -292,7 +288,7 @@
    * when the polynomial value is representable. */
   template <typename Dest, typename Rhs, typename ProductScalar>
   void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
-    eigen_assert(rhs.rows() == m_cols && "invalid product: dimensions do not match");
+    EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE_MSG(*this, rhs, "invalid product: dimensions do not match");
     // A unit alpha must not multiply: even the identity complex scalar (1,0)
     // pollutes an (Inf,0) value with NaN through the 0*Inf cross term.
     const bool unitAlpha = alpha == ProductScalar(1);
@@ -416,8 +412,9 @@
    * nodes. */
   template <int Rows_, int Cols_>
   BjorckPereyra& compute(const Vandermonde<Scalar, Rows_, Cols_>& V) {
-    EIGEN_STATIC_ASSERT(Rows_ == Dynamic || Cols_ == Dynamic || Rows_ == Cols_, YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
-    eigen_assert(V.rows() == V.cols() && "BjorckPereyra requires a square Vandermonde matrix");
+    EIGEN_INTERNAL_CHECK_SHAPE(V.rows() == V.cols() && "BjorckPereyra requires a square Vandermonde matrix",
+                               YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES,
+                               internal::square_dimensions<Vandermonde<Scalar, Rows_, Cols_>>);
     m_x = V.nodes();
     m_order.clear();
     m_info = Success;
@@ -462,7 +459,7 @@
   void _solve_impl(const RhsType& rhs, DstType& dst) const {
     using RhsScalar = typename RhsType::Scalar;
     using WorkScalar = typename DstType::Scalar;
-    using ProductOp = internal::scalar_product_op<Scalar, RhsScalar>;
+    using ProductOp EIGEN_UNUSED = internal::scalar_product_op<Scalar, RhsScalar>;
     EIGEN_CHECK_BINARY_COMPATIBILITY(ProductOp, Scalar, RhsScalar)
 
     const Index n = m_x.size();
@@ -489,7 +486,7 @@
   void _solve_impl_transposed(const RhsType& rhs, DstType& dst) const {
     using RhsScalar = typename RhsType::Scalar;
     using WorkScalar = typename DstType::Scalar;
-    using ProductOp = internal::scalar_product_op<Scalar, RhsScalar>;
+    using ProductOp EIGEN_UNUSED = internal::scalar_product_op<Scalar, RhsScalar>;
     EIGEN_CHECK_BINARY_COMPATIBILITY(ProductOp, Scalar, RhsScalar)
 
     const Index n = m_x.size();
diff --git a/contrib/Eigen/src/Tensor/TensorAssign.h b/contrib/Eigen/src/Tensor/TensorAssign.h
index 0093c24..ac2972a 100644
--- a/contrib/Eigen/src/Tensor/TensorAssign.h
+++ b/contrib/Eigen/src/Tensor/TensorAssign.h
@@ -126,7 +126,7 @@
   }
 
   EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType) {
-    eigen_assert(dimensions_match(m_leftImpl.dimensions(), m_rightImpl.dimensions()));
+    EIGEN_INTERNAL_ASSERT_TENSOR_SHAPE(m_leftImpl, m_rightImpl);
     m_leftImpl.evalSubExprsIfNeeded(nullptr);
     // If the lhs provides raw access to its storage area (i.e. if m_leftImpl.data() returns a non
     // null value), attempt to evaluate the rhs expression in place. Returns true iff in place
diff --git a/contrib/Eigen/src/Tensor/TensorDimensions.h b/contrib/Eigen/src/Tensor/TensorDimensions.h
index 2ea34f9..1973eda 100644
--- a/contrib/Eigen/src/Tensor/TensorDimensions.h
+++ b/contrib/Eigen/src/Tensor/TensorDimensions.h
@@ -309,6 +309,21 @@
   return -1;
 }
 
+// Fixed rank (DSizes) does not imply fixed extents (Sizes).
+template <typename Dims1, typename Dims2>
+struct tensor_dimension_relation {
+  static constexpr bool Compatible = array_size<Dims1>::value == array_size<Dims2>::value;
+  static constexpr bool Known = !Compatible;
+  static constexpr bool Proven = false;
+};
+
+template <std::ptrdiff_t... Left, std::ptrdiff_t... Right>
+struct tensor_dimension_relation<Sizes<Left...>, Sizes<Right...>> {
+  static constexpr bool Known = true;
+  static constexpr bool Compatible = std::is_same<Sizes<Left...>, Sizes<Right...>>::value;
+  static constexpr bool Proven = Compatible;
+};
+
 template <typename Dims1, typename Dims2, ptrdiff_t n, ptrdiff_t m>
 struct sizes_match_below_dim {
   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool run(const Dims1&, const Dims2&) { return false; }
@@ -335,4 +350,11 @@
 
 }  // end namespace Eigen
 
+#define EIGEN_INTERNAL_ASSERT_TENSOR_SHAPE(Lhs, Rhs)                                                                   \
+  EIGEN_INTERNAL_CHECK_SHAPE(                                                                                          \
+      Eigen::dimensions_match((Lhs).dimensions(), (Rhs).dimensions()) && "Both tensors must have the same dimensions", \
+      YOU_MIXED_TENSORS_OF_DIFFERENT_SIZES,                                                                            \
+      Eigen::internal::tensor_dimension_relation<Eigen::internal::remove_all_t<decltype((Lhs).dimensions())>,          \
+                                                 Eigen::internal::remove_all_t<decltype((Rhs).dimensions())>>)
+
 #endif  // EIGEN_TENSOR_TENSOR_DIMENSIONS_H
diff --git a/contrib/Eigen/src/Tensor/TensorEvaluator.h b/contrib/Eigen/src/Tensor/TensorEvaluator.h
index 023f7f3..7fe6965 100644
--- a/contrib/Eigen/src/Tensor/TensorEvaluator.h
+++ b/contrib/Eigen/src/Tensor/TensorEvaluator.h
@@ -700,7 +700,7 @@
                              static_cast<int>(TensorEvaluator<RightArgType, Device>::Layout) ||
                          internal::traits<XprType>::NumDimensions <= 1),
                         YOU_MADE_A_PROGRAMMING_MISTAKE);
-    eigen_assert(dimensions_match(m_leftImpl.dimensions(), m_rightImpl.dimensions()));
+    EIGEN_INTERNAL_ASSERT_TENSOR_SHAPE(m_leftImpl, m_rightImpl);
   }
 
   typedef typename XprType::Index Index;
@@ -830,8 +830,8 @@
         (std::is_same<typename internal::traits<Arg1Type>::Index, typename internal::traits<Arg3Type>::Index>::value),
         STORAGE_INDEX_MUST_MATCH)
 
-    eigen_assert(dimensions_match(m_arg1Impl.dimensions(), m_arg2Impl.dimensions()) &&
-                 dimensions_match(m_arg1Impl.dimensions(), m_arg3Impl.dimensions()));
+    EIGEN_INTERNAL_ASSERT_TENSOR_SHAPE(m_arg1Impl, m_arg2Impl);
+    EIGEN_INTERNAL_ASSERT_TENSOR_SHAPE(m_arg1Impl, m_arg3Impl);
   }
 
   typedef typename XprType::Index Index;
@@ -973,8 +973,8 @@
     EIGEN_STATIC_ASSERT((static_cast<int>(TensorEvaluator<IfArgType, Device>::Layout) ==
                          static_cast<int>(TensorEvaluator<ElseArgType, Device>::Layout)),
                         YOU_MADE_A_PROGRAMMING_MISTAKE);
-    eigen_assert(dimensions_match(m_condImpl.dimensions(), m_thenImpl.dimensions()));
-    eigen_assert(dimensions_match(m_thenImpl.dimensions(), m_elseImpl.dimensions()));
+    EIGEN_INTERNAL_ASSERT_TENSOR_SHAPE(m_condImpl, m_thenImpl);
+    EIGEN_INTERNAL_ASSERT_TENSOR_SHAPE(m_thenImpl, m_elseImpl);
   }
 
   typedef typename XprType::Index Index;
diff --git a/contrib/test/CMakeLists.txt b/contrib/test/CMakeLists.txt
index d63df6a..3186340 100644
--- a/contrib/test/CMakeLists.txt
+++ b/contrib/test/CMakeLists.txt
@@ -203,6 +203,8 @@
 ei_add_test(tensor_expr)
 ei_add_test(tensor_fft)
 ei_add_test(tensor_fixed_size)
+ei_add_test(tensor_shape_checks)
+ei_add_test(tensor_shape_checks_no_static_assert)
 ei_add_test(tensor_forced_eval)
 ei_add_test(tensor_generator)
 ei_add_test(tensor_ifft)
diff --git a/contrib/test/tensor_shape_checks.cpp b/contrib/test/tensor_shape_checks.cpp
new file mode 100644
index 0000000..33add89
--- /dev/null
+++ b/contrib/test/tensor_shape_checks.cpp
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include "main.h"
+#include <contrib/Eigen/Tensor>
+
+EIGEN_DECLARE_TEST(tensor_shape_checks) {
+  using namespace Eigen;
+  STATIC_CHECK((internal::tensor_dimension_relation<Sizes<2, 3>, Sizes<2, 3>>::Proven));
+  STATIC_CHECK((!internal::tensor_dimension_relation<Sizes<2, 3>, Sizes<3, 2>>::Compatible));
+  STATIC_CHECK((!internal::tensor_dimension_relation<DSizes<Index, 2>, DSizes<Index, 2>>::Proven));
+  STATIC_CHECK((!internal::tensor_dimension_relation<Sizes<2, 3>, DSizes<Index, 2>>::Proven));
+  STATIC_CHECK((!internal::tensor_dimension_relation<DSizes<Index, 2>, DSizes<Index, 3>>::Compatible));
+  TensorFixedSize<double, Sizes<2, 3>> a, b, result;
+  a.setConstant(2);
+  b.setConstant(3);
+  result = a + b;
+  for (Index i = 0; i < result.size(); ++i) VERIFY_IS_EQUAL(result.data()[i], 5);
+  Tensor<double, 2> dynamic(2, 3), wrong(3, 2);
+  dynamic = a;
+  result = a + dynamic;
+  for (Index i = 0; i < result.size(); ++i) VERIFY_IS_EQUAL(result.data()[i], 4);
+  VERIFY_RAISES_ASSERT(result = a + wrong);
+#ifdef EIGEN_NO_STATIC_ASSERT
+  TensorFixedSize<double, Sizes<3, 2>> wrong_fixed;
+  VERIFY_RAISES_ASSERT(result = a + wrong_fixed);
+  Tensor<double, 3> wrong_rank(2, 3, 1);
+  VERIFY_RAISES_ASSERT(EIGEN_INTERNAL_ASSERT_TENSOR_SHAPE(a, wrong_rank));
+#endif
+}
diff --git a/contrib/test/tensor_shape_checks_no_static_assert.cpp b/contrib/test/tensor_shape_checks_no_static_assert.cpp
new file mode 100644
index 0000000..e08c438
--- /dev/null
+++ b/contrib/test/tensor_shape_checks_no_static_assert.cpp
@@ -0,0 +1,4 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#define EIGEN_NO_STATIC_ASSERT
+#include "tensor_shape_checks.cpp"  // NOLINT(bugprone-suspicious-include): Reuse the suite with static assertions disabled.
diff --git a/doc/TopicAssertions.dox b/doc/TopicAssertions.dox
index 5e1bb08..4773ffe 100644
--- a/doc/TopicAssertions.dox
+++ b/doc/TopicAssertions.dox
@@ -25,6 +25,11 @@
 
 \section StaticAssert Static assertions
 
+Coefficient-count checks against matrix rows or columns reject provably incompatible fixed dimensions at compile
+time, including in release builds. This applies to diagonal products, factorization workspaces and rank updates.
+Unknown dimensions retain debug runtime checks, also when \c EIGEN_NO_STATIC_ASSERT disables static diagnostics.
+Invalid fixed-size calls that previously compiled and asserted only at runtime can therefore fail to compile.
+
 In the Eigen library, there are many conditions that can and should be detected at compile time. For instance, we use static assertions to prevent the code below from compiling.
 
 \code
@@ -86,7 +91,38 @@
 \endcode
 
 They are not downgraded to run-time checks, so the misuse they guard against is reported neither at compile time
-nor at run time. \c EIGEN_NO_STATIC_ASSERT is undefined by default.
+nor at run time unless the operation also has a runtime assertion.
+\c EIGEN_NO_STATIC_ASSERT is undefined by default.
+
+\subsection ShapeAssertions Shape preconditions
+
+Shared shape checks reject statically incompatible coefficient counts and product inner dimensions at compile time,
+including in release builds. Checks proven valid do not read dimensions at runtime.
+Matching rows and columns (including directional concatenation checks) follow the same rule.
+Vector adaptation and scalar extraction likewise reject provably invalid shapes, while
+allowing dynamically sized inputs that satisfy their preconditions at runtime.
+Square-only preconditions remain runtime checks, elided when the matrix is proven square.
+This preserves runtime-guarded calls on rectangular fixed-size types in C++14.
+Solver right-hand sides are checked against the appropriate fixed dimension, including
+the reversed row/column rule for transposed solves.
+Nonempty-input checks remain runtime-only so C++14 code can guard reductions on fixed empty expressions.
+They are elided for inputs proven nonempty. Empty dense
+\c sum() and \c prod() remain valid and return their respective identity values.
+Empty \c lpNorm<Infinity>() and \c hypotNorm() likewise remain valid and return zero.
+For an empty vector, \c stableNormalized() returns an empty vector and \c stableNormalize() is a no-op;
+the internal maximum-absolute-value reduction used by stable normalization has identity zero.
+An empty vector's \c homogeneous() expression is not empty: it contains the appended coefficient one,
+so its \c sum() and \c prod() return one rather than asserting on the underlying empty vector.
+These identity reductions are valid for fixed-size empty inputs in C++14 as well as dynamic inputs.
+Tensor shape checks reject different ranks or incompatible fixed extents. Equal rank alone
+never proves equality of runtime extents.
+Unknown dimensions are checked by debug assertions; maximum storage capacities are
+not treated as actual dimensions. Row and column vectors may have equal coefficient counts.
+
+With \c EIGEN_NO_STATIC_ASSERT, these shared checks retain their debug runtime fallback.
+Defining \c EIGEN_NO_DEBUG disables the default runtime assertions, not the static diagnostics.
+This stricter validation can reject invalid code that previously compiled and only asserted
+when executed in a debug build.
 
 */
 }
diff --git a/failtest/CMakeLists.txt b/failtest/CMakeLists.txt
index c42e6b6..7238008 100644
--- a/failtest/CMakeLists.txt
+++ b/failtest/CMakeLists.txt
@@ -111,4 +111,17 @@
                         CXX_EXTENSIONS OFF)
 endforeach()
 
+ei_add_failtest("reshape_fixed_count")
+ei_add_failtest("shape_same_size")
+ei_add_failtest("shape_product")
+ei_add_failtest("shape_rank_update")
+ei_add_failtest("shape_same_rows")
+ei_add_failtest("shape_same_cols")
+ei_add_failtest("shape_scalar")
+ei_add_failtest("shape_solver_rhs")
+ei_add_failtest("shape_tensor")
+ei_add_failtest("resize_like_vector_shape")
+
+ei_add_failtest("shape_size_rows")
+ei_add_failtest("shape_size_cols")
 ei_add_failtest_fixture()
diff --git a/failtest/reshape_fixed_count.cpp b/failtest/reshape_fixed_count.cpp
new file mode 100644
index 0000000..8fe2230
--- /dev/null
+++ b/failtest/reshape_fixed_count.cpp
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+
+int main() {
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::Vector3d vector = Eigen::Vector3d::Ones();
+#else
+  Eigen::Vector4d vector = Eigen::Vector4d::Ones();
+#endif
+  const auto reshaped = vector.reshaped(Eigen::fix<2>, Eigen::fix<2>);
+  const Eigen::Reshaped<decltype(vector), 2, 2> direct(vector);
+  return reshaped.rows() != 2 || direct.rows() != 2;
+}
diff --git a/failtest/resize_like_vector_shape.cpp b/failtest/resize_like_vector_shape.cpp
new file mode 100644
index 0000000..e52edf5
--- /dev/null
+++ b/failtest/resize_like_vector_shape.cpp
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+
+int main() {
+  Eigen::VectorXd destination;
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::Matrix2d source;
+#else
+  Eigen::RowVector2d source;
+#endif
+  destination.resizeLike(source);
+  return destination.size() != source.size();
+}
diff --git a/failtest/shape_product.cpp b/failtest/shape_product.cpp
new file mode 100644
index 0000000..5334135
--- /dev/null
+++ b/failtest/shape_product.cpp
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+
+int main() {
+  Eigen::Matrix<double, 2, 3> lhs;
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::Matrix<double, 4, 2> rhs;
+#else
+  Eigen::Matrix<double, 3, 2> rhs;
+#endif
+  const Eigen::Product<decltype(lhs), decltype(rhs)> product(lhs, rhs);
+  return product.rows() != 2;
+}
diff --git a/failtest/shape_rank_update.cpp b/failtest/shape_rank_update.cpp
new file mode 100644
index 0000000..b140658
--- /dev/null
+++ b/failtest/shape_rank_update.cpp
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+int main() {
+  Eigen::Matrix2d value = Eigen::Matrix2d::Zero();
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::Matrix<double, 3, 2> rhs;
+#else
+  Eigen::Matrix2d rhs;
+#endif
+  rhs.setZero();
+  value.selfadjointView<Eigen::Lower>().rankUpdate(rhs);
+}
diff --git a/failtest/shape_same_cols.cpp b/failtest/shape_same_cols.cpp
new file mode 100644
index 0000000..0632615
--- /dev/null
+++ b/failtest/shape_same_cols.cpp
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+int main() {
+  Eigen::Matrix<double, 2, 3> lhs;
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::Matrix<double, 4, 4> rhs;
+#else
+  Eigen::Matrix<double, 4, 3> rhs;
+#endif
+  const auto result = Eigen::vcat(lhs, rhs);
+  return result.rows() != 6;
+}
diff --git a/failtest/shape_same_rows.cpp b/failtest/shape_same_rows.cpp
new file mode 100644
index 0000000..b18a6c6
--- /dev/null
+++ b/failtest/shape_same_rows.cpp
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+int main() {
+  Eigen::Matrix<double, 2, 3> lhs;
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::Matrix<double, 3, 4> rhs;
+#else
+  Eigen::Matrix<double, 2, 4> rhs;
+#endif
+  const auto result = Eigen::hcat(lhs, rhs);
+  return result.cols() != 7;
+}
diff --git a/failtest/shape_same_size.cpp b/failtest/shape_same_size.cpp
new file mode 100644
index 0000000..51b8383
--- /dev/null
+++ b/failtest/shape_same_size.cpp
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+
+int main() {
+  Eigen::Vector3d lhs = Eigen::Vector3d::Ones();
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::RowVector4d rhs = Eigen::RowVector4d::Ones();
+#else
+  Eigen::RowVector3d rhs = Eigen::RowVector3d::Ones();
+#endif
+  return lhs.dot(rhs) != 3;
+}
diff --git a/failtest/shape_scalar.cpp b/failtest/shape_scalar.cpp
new file mode 100644
index 0000000..86ebfa8
--- /dev/null
+++ b/failtest/shape_scalar.cpp
@@ -0,0 +1,11 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+int main() {
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::Matrix2d value = Eigen::Matrix2d::Zero();
+#else
+  Eigen::Matrix<double, 1, 1> value = Eigen::Matrix<double, 1, 1>::Zero();
+#endif
+  return value.value() != 0;
+}
diff --git a/failtest/shape_size_cols.cpp b/failtest/shape_size_cols.cpp
new file mode 100644
index 0000000..65622ae
--- /dev/null
+++ b/failtest/shape_size_cols.cpp
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+int main() {
+  Eigen::Matrix<double, 3, 4> matrix = Eigen::Matrix<double, 3, 4>::Ones();
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::Vector3d vector = Eigen::Vector3d::Ones();
+#else
+  Eigen::Vector4d vector = Eigen::Vector4d::Ones();
+#endif
+  const Eigen::MatrixXd result = matrix * vector.asDiagonal();
+  return result.sum() != 12;
+}
diff --git a/failtest/shape_size_rows.cpp b/failtest/shape_size_rows.cpp
new file mode 100644
index 0000000..3a050f5
--- /dev/null
+++ b/failtest/shape_size_rows.cpp
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/Core>
+int main() {
+  Eigen::Matrix<double, 3, 4> matrix = Eigen::Matrix<double, 3, 4>::Ones();
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::RowVector4d vector = Eigen::RowVector4d::Ones();
+#else
+  Eigen::RowVector3d vector = Eigen::RowVector3d::Ones();
+#endif
+  const Eigen::MatrixXd result = vector.asDiagonal() * matrix;
+  return result.sum() != 12;
+}
diff --git a/failtest/shape_solver_rhs.cpp b/failtest/shape_solver_rhs.cpp
new file mode 100644
index 0000000..9955d95
--- /dev/null
+++ b/failtest/shape_solver_rhs.cpp
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <Eigen/LU>
+int main() {
+  Eigen::Matrix<double, 2, 3> matrix = Eigen::Matrix<double, 2, 3>::Zero();
+  const auto solver = matrix.fullPivLu();
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::Vector2d rhs = Eigen::Vector2d::Ones();
+#else
+  Eigen::Vector3d rhs = Eigen::Vector3d::Ones();
+#endif
+  const auto solution = solver.transpose().solve(rhs);
+  return solution.rows() != 2;
+}
diff --git a/failtest/shape_tensor.cpp b/failtest/shape_tensor.cpp
new file mode 100644
index 0000000..1c18d60
--- /dev/null
+++ b/failtest/shape_tensor.cpp
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include <contrib/Eigen/Tensor>
+int main() {
+  Eigen::TensorFixedSize<double, Eigen::Sizes<2, 3>> lhs;
+#ifdef EIGEN_SHOULD_FAIL_TO_BUILD
+  Eigen::TensorFixedSize<double, Eigen::Sizes<3, 2>> rhs;
+#else
+  Eigen::TensorFixedSize<double, Eigen::Sizes<2, 3>> rhs;
+#endif
+  lhs.setConstant(2);
+  rhs.setConstant(3);
+  Eigen::TensorFixedSize<double, Eigen::Sizes<2, 3>> result;
+  result = lhs + rhs;
+  return result(0, 0) != 5;
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 874d5b8..f17cdef 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -271,6 +271,15 @@
 ei_add_test(realview)
 ei_add_test(complex_components)
 ei_add_test(meta)
+ei_add_test(shape_checks)
+ei_add_test(shape_check_messages)
+ei_add_test(shape_checks_no_static_assert)
+ei_add_test(shape_checks_modules)
+ei_add_test(shape_checks_modules_no_static_assert)
+ei_add_test(shape_size_axis)
+ei_add_test(shape_size_axis_no_static_assert)
+ei_add_test(shape_size_axis_applications)
+ei_add_test(shape_checks_compatibility)
 ei_add_test(preprocessor_directives)
 ei_add_test(lru_cache)
 ei_add_test(maxsizevector)
diff --git a/test/commainitializer.cpp b/test/commainitializer.cpp
index e0ae722..af953b5 100644
--- a/test/commainitializer.cpp
+++ b/test/commainitializer.cpp
@@ -68,6 +68,12 @@
 };
 
 void test_basics() {
+  MatrixXd empty_rows(0, 2), empty_cols(2, 0);
+  VERIFY_RAISES_ASSERT((empty_rows << 1.0));
+  VERIFY_RAISES_ASSERT((empty_cols << 1.0));
+  Matrix<double, 0, 2> fixed_empty;
+  VERIFY_RAISES_ASSERT((fixed_empty << 1.0));
+
   Matrix3d m3;
   Matrix4d m4;
 
diff --git a/test/lu.cpp b/test/lu.cpp
index 6de7859..ad73632 100644
--- a/test/lu.cpp
+++ b/test/lu.cpp
@@ -296,8 +296,8 @@
   VERIFY_RAISES_ASSERT(lu.kernel())
   VERIFY_RAISES_ASSERT(lu.image(tmp))
   VERIFY_RAISES_ASSERT(lu.solve(tmp))
-  VERIFY_RAISES_ASSERT(lu.transpose().solve(tmp))
-  VERIFY_RAISES_ASSERT(lu.adjoint().solve(tmp))
+  VERIFY_RAISES_ASSERT(lu.transpose().solve(tmp.transpose()))
+  VERIFY_RAISES_ASSERT(lu.adjoint().solve(tmp.transpose()))
   VERIFY_RAISES_ASSERT(lu.determinant())
   VERIFY_RAISES_ASSERT(lu.absDeterminant())
   VERIFY_RAISES_ASSERT(lu.logAbsDeterminant())
@@ -313,8 +313,8 @@
   VERIFY_RAISES_ASSERT(plu.matrixLU())
   VERIFY_RAISES_ASSERT(plu.permutationP())
   VERIFY_RAISES_ASSERT(plu.solve(tmp))
-  VERIFY_RAISES_ASSERT(plu.transpose().solve(tmp))
-  VERIFY_RAISES_ASSERT(plu.adjoint().solve(tmp))
+  VERIFY_RAISES_ASSERT(plu.transpose().solve(tmp.transpose()))
+  VERIFY_RAISES_ASSERT(plu.adjoint().solve(tmp.transpose()))
   VERIFY_RAISES_ASSERT(plu.determinant())
   VERIFY_RAISES_ASSERT(plu.absDeterminant())
   VERIFY_RAISES_ASSERT(plu.logAbsDeterminant())
diff --git a/test/shape_check_messages.cpp b/test/shape_check_messages.cpp
new file mode 100644
index 0000000..2ceeff7
--- /dev/null
+++ b/test/shape_check_messages.cpp
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#ifndef EIGEN_DEBUG_ASSERTS
+#define EIGEN_DEBUG_ASSERTS 1
+#endif
+#include "main.h"
+#include <Eigen/IterativeLinearSolvers>
+#include <Eigen/SparseQR>
+
+#if !defined(EIGEN_NO_ASSERTION_CHECKING)
+template <typename Func>
+void check_shape_assertion_message(const char* message, const Func& operation) {
+  Eigen::eigen_assert_list.clear();
+  Eigen::internal::push_assert = true;
+  operation();
+  Eigen::internal::push_assert = false;
+  bool found = false;
+  for (const auto& assertion : Eigen::eigen_assert_list) {
+    found = found || assertion.find(message) != std::string::npos;
+  }
+  VERIFY(found);
+}
+#endif
+
+EIGEN_DECLARE_TEST(shape_check_messages) {
+#if !defined(EIGEN_NO_ASSERTION_CHECKING)
+  MatrixXd matrix(1, 1);
+  check_shape_assertion_message("Cannot comma-initialize a 0x0 matrix (operator<<)", [&] { matrix << 1.0; });
+
+  DiagonalPreconditioner<double> preconditioner(matrix);
+  const VectorXd rhs = VectorXd::Ones(1);
+  check_shape_assertion_message("DiagonalPreconditioner::solve()", [&] { (void)preconditioner.solve(rhs); });
+
+  SparseMatrix<double> sparse = matrix.sparseView();
+  SparseQR<SparseMatrix<double>, COLAMDOrdering<int>> qr(sparse);
+  check_shape_assertion_message("SparseQR::solve()", [&] { (void)qr.solve(rhs); });
+
+  BlockSparseMatrix<double, ColMajor, 2, 2> blocks(1, 1);
+  blocks.setIdentity();
+  const VectorXd block_rhs = VectorXd::Ones(2);
+  check_shape_assertion_message("BlockSparseTriangularView * Dense: dimension mismatch", [&] {
+    const VectorXd result = blocks.triangularView<Lower>() * block_rhs;
+    VERIFY_IS_EQUAL(result, block_rhs);
+  });
+#endif
+}
diff --git a/test/shape_checks.cpp b/test/shape_checks.cpp
new file mode 100644
index 0000000..c1bad9b
--- /dev/null
+++ b/test/shape_checks.cpp
@@ -0,0 +1,320 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+
+#include "main.h"
+#include <Eigen/Dense>
+#include <contrib/Eigen/MatrixFunctions>
+
+struct MissingShape {};
+struct PartialShape {};
+template <int Rows, int Cols>
+struct ShapeProbe;
+namespace Eigen {
+namespace internal {
+template <int Rows, int Cols>
+struct traits<ShapeProbe<Rows, Cols>> : traits<Matrix<double, Rows, Cols>> {};
+template <>
+struct traits<PartialShape> {
+  static constexpr int RowsAtCompileTime = 3;
+};
+}  // namespace internal
+}  // namespace Eigen
+
+template <int Rows, int Cols>
+struct ShapeProbe : MatrixBase<ShapeProbe<Rows, Cols>> {
+  using Nested = const ShapeProbe&;
+  mutable int row_reads = 0;
+  mutable int col_reads = 0;
+  Index rows() const {
+    ++row_reads;
+    return 2;
+  }
+  Index cols() const {
+    ++col_reads;
+    return 2;
+  }
+};
+
+void rank_update_shape_checks() {
+  Matrix2d b = Matrix2d::Constant(2);
+  Matrix2d updated = Matrix2d::Zero();
+  updated.selfadjointView<Lower>().rankUpdate(b);
+  VERIFY_IS_EQUAL(Matrix2d(updated.selfadjointView<Lower>()), (b * b.transpose()).eval());
+  MatrixXd wrong = MatrixXd::Ones(3, 2);
+  VERIFY_RAISES_ASSERT(updated.selfadjointView<Lower>().rankUpdate(wrong));
+#ifdef EIGEN_NO_STATIC_ASSERT
+  Matrix<double, 3, 2> fixed_wrong = wrong;
+  VERIFY_RAISES_ASSERT(updated.selfadjointView<Lower>().rankUpdate(fixed_wrong));
+#endif
+}
+
+void product_coefficient_shape_checks() {
+  Matrix2d a = Matrix2d::Identity(), b = Matrix2d::Constant(2);
+  VERIFY_IS_EQUAL(a.lazyProduct(b).coeff(0, 1), 2);
+  Vector2d v = Vector2d::Ones();
+  VERIFY_IS_EQUAL((v.transpose() * v).coeff(0, 0), 2);
+  VERIFY_IS_EQUAL((v.transpose() * v).coeff(0), 2);
+  MatrixXd dynamic_a = a, dynamic_b = b;
+  VERIFY_RAISES_ASSERT((dynamic_a * dynamic_b).coeff(0, 0));
+  VERIFY_RAISES_ASSERT((dynamic_a * dynamic_b).coeff(0));
+  MatrixXd scalar = MatrixXd::Constant(1, 1, 3);
+  VERIFY_IS_EQUAL((scalar * scalar).coeff(0), 9);
+}
+
+void transpose_shape_checks() {
+  Matrix<double, 2, 3> rectangle;
+  rectangle << 1, 2, 3, 4, 5, 6;
+  MatrixXd resizable = rectangle;
+  resizable.transposeInPlace();
+  VERIFY_IS_EQUAL(resizable, rectangle.transpose());
+  Matrix<double, 2, Dynamic> partial = Matrix2d::Identity();
+  partial.transposeInPlace();
+  VERIFY_IS_EQUAL(partial, Matrix2d::Identity());
+  partial = rectangle;
+  VERIFY_RAISES_ASSERT(partial.transposeInPlace());
+#ifdef EIGEN_NO_STATIC_ASSERT
+  VERIFY_RAISES_ASSERT(rectangle.transposeInPlace());
+#endif
+}
+
+void product_shape_checks() {
+  transpose_shape_checks();
+  product_coefficient_shape_checks();
+  rank_update_shape_checks();
+  ShapeProbe<2, 2> a, b;
+  const auto product = a * b;
+  EIGEN_UNUSED_VARIABLE(product);
+  VERIFY_IS_EQUAL(a.col_reads + b.row_reads, 0);
+  const MatrixBase<ShapeProbe<2, 2>>& base_a = a;
+  const MatrixBase<ShapeProbe<2, 2>>& base_b = b;
+  EIGEN_INTERNAL_ASSERT_PRODUCT_SHAPE(base_a, base_b);
+  VERIFY_IS_EQUAL(a.col_reads + b.row_reads, 0);
+  Matrix2d fixed = Matrix2d::Identity();
+  MatrixXd wrong(3, 2);
+  VERIFY_RAISES_ASSERT((fixed * wrong));
+#ifdef EIGEN_NO_STATIC_ASSERT
+  Matrix<double, 3, 2> fixed_wrong;
+  VERIFY_RAISES_ASSERT((Product<Matrix2d, decltype(fixed_wrong)>(fixed, fixed_wrong)));
+#endif
+  Transpositions<2> swaps;
+  swaps.setIdentity();
+  VERIFY_IS_EQUAL((swaps * fixed).eval(), fixed);
+  VERIFY_IS_EQUAL((fixed * swaps.transpose()).eval(), fixed);
+}
+
+template <int Size, int Order>
+void product_destination_shape_checks() {
+  using MatrixType = Matrix<double, Size, Size, Order>;
+  const MatrixType identity = MatrixType::Identity(3, 3);
+  const MatrixType values = MatrixType::Constant(3, 3, 2);
+  MatrixType result = MatrixType::Zero(3, 3);
+  result.noalias() += identity * values;
+  VERIFY_IS_EQUAL(result, values);
+  result.noalias() -= identity * values;
+  VERIFY(result.isZero());
+  result.noalias() += identity.template selfadjointView<Lower>() * values;
+  VERIFY_IS_EQUAL(result, values);
+  result.noalias() -= identity.template triangularView<Upper>() * values;
+  VERIFY(result.isZero());
+
+  PermutationMatrix<Size> permutation(3);
+  permutation.setIdentity();
+  DiagonalMatrix<double, Size> diagonal(3);
+  diagonal.diagonal().setConstant(2);
+  result.noalias() = identity.template triangularView<Lower>() * diagonal;
+  VERIFY_IS_EQUAL(result, 2 * identity);
+  result.noalias() = diagonal * identity.template triangularView<Upper>();
+  VERIFY_IS_EQUAL(result, 2 * identity);
+  result.noalias() = identity.template selfadjointView<Lower>() * diagonal;
+  VERIFY_IS_EQUAL(result, 2 * identity);
+  result.setZero();
+  const auto scaled = permutation * diagonal;
+  result += scaled;
+  VERIFY_IS_EQUAL(result, 2 * identity);
+  result -= scaled;
+  VERIFY(result.isZero());
+  MatrixXd wrong_destination = MatrixXd::Zero(2, 3);
+  VERIFY_RAISES_ASSERT(wrong_destination.noalias() += identity * values);
+  VERIFY_RAISES_ASSERT(wrong_destination.noalias() -= identity * values);
+  VERIFY_RAISES_ASSERT(wrong_destination += scaled);
+  VERIFY_RAISES_ASSERT(wrong_destination -= scaled);
+  result.noalias() += scaled * identity;
+  VERIFY_IS_EQUAL(result, 2 * identity);
+  result.noalias() -= scaled * identity;
+  VERIFY(result.isZero());
+  VERIFY_IS_EQUAL((scaled * permutation).toDenseMatrix(), 2 * identity);
+  VERIFY_IS_EQUAL((permutation * scaled).toDenseMatrix(), 2 * identity);
+  VERIFY_IS_EQUAL((permutation * permutation.inverse()).toDenseMatrix().template cast<double>(), identity);
+  const ScaledPermutationMatrix<double, Size> from_coefficients(permutation.indices(), diagonal.diagonal());
+  VERIFY_IS_EQUAL(from_coefficients.toDenseMatrix(), 2 * identity);
+  DiagonalMatrix<double, Dynamic> wrong(2);
+  VERIFY_RAISES_ASSERT(permutation * wrong);
+}
+
+EIGEN_DECLARE_TEST(shape_checks) {
+  ShapeProbe<2, 2> fixed_a, fixed_b;
+  const auto sum = fixed_a + fixed_b;
+  EIGEN_UNUSED_VARIABLE(sum);
+  internal::resize_if_allowed(fixed_a, fixed_b, internal::add_assign_op<double, double>());
+  VERIFY_IS_EQUAL(fixed_a.row_reads + fixed_b.row_reads + fixed_a.col_reads + fixed_b.col_reads, 0);
+  ShapeProbe<2, Dynamic> partial_a, partial_b;
+  const auto partial_sum = partial_a + partial_b;
+  EIGEN_UNUSED_VARIABLE(partial_sum);
+  VERIFY_IS_EQUAL(partial_a.row_reads + partial_b.row_reads, 0);
+#ifndef EIGEN_NO_DEBUG
+  VERIFY(partial_a.col_reads > 0 && partial_b.col_reads > 0);
+#endif
+  Matrix2d matrix = Matrix2d::Identity();
+  MatrixXd bad(3, 2);
+  VERIFY_RAISES_ASSERT((matrix + bad));
+  VERIFY_RAISES_ASSERT(matrix += bad);
+  VERIFY_IS_EQUAL(hcat(matrix, matrix).cols(), 4);
+  VERIFY_IS_EQUAL(vcat(matrix, matrix).rows(), 4);
+  Matrix<double, 2, 3> concat_wide = Matrix<double, 2, 3>::Ones();
+  VERIFY_IS_EQUAL(hcat(matrix, concat_wide).cols(), 5);
+  VERIFY_IS_EQUAL(vcat(matrix, concat_wide.transpose()).rows(), 5);
+#ifdef EIGEN_NO_STATIC_ASSERT
+  VERIFY_RAISES_ASSERT(hcat(matrix, concat_wide.transpose()));
+  VERIFY_RAISES_ASSERT(vcat(matrix, concat_wide));
+#endif
+  Matrix<double, 2, 3> wide = Matrix<double, 2, 3>::Ones();
+  const DiagonalMatrix<double, 2> left_diagonal(Vector2d::Constant(2));
+  const DiagonalMatrix<double, 3> right_diagonal(Vector3d::Constant(3));
+  Matrix<double, 2, 3> triangular_product = left_diagonal * wide.triangularView<Upper>();
+  VERIFY_IS_EQUAL(triangular_product, 2 * wide.triangularView<Upper>().toDenseMatrix());
+  triangular_product = wide.triangularView<Upper>() * right_diagonal;
+  VERIFY_IS_EQUAL(triangular_product, 3 * wide.triangularView<Upper>().toDenseMatrix());
+  Vector3d vector = Vector3d::Ones();
+  RowVector3d row = RowVector3d::Ones();
+  VERIFY_IS_EQUAL(vector.dot(row), 3);
+  VERIFY_IS_EQUAL(vector.reshaped(fix<1>, fix<3>).sum(), 3);
+  VectorXd dynamic_reshape = vector;
+  VERIFY_RAISES_ASSERT(dynamic_reshape.reshaped(fix<2>, fix<2>));
+#ifdef EIGEN_NO_STATIC_ASSERT
+  VERIFY_RAISES_ASSERT(vector.reshaped(fix<2>, fix<2>));
+  Vector4d mismatched_vector = Vector4d::Ones();
+  VERIFY_RAISES_ASSERT(vector.dot(mismatched_vector));
+#endif
+  VectorXd bad_vector(2);
+  VERIFY_RAISES_ASSERT(vector.dot(bad_vector));
+  VectorXd resized;
+  resized.resizeLike(row);
+  VERIFY_IS_EQUAL(resized.size(), 3);
+  MatrixXd resize_source = matrix;
+  VERIFY_RAISES_ASSERT(resized.resizeLike(resize_source));
+#ifdef EIGEN_NO_STATIC_ASSERT
+  VERIFY_RAISES_ASSERT(resized.resizeLike(matrix));
+#endif
+  Ref<VectorXd> ref(vector);
+  Ref<Matrix2d> matrix_ref(matrix);
+  VERIFY_IS_EQUAL(matrix_ref.rows(), 2);
+  Matrix2d resized_matrix;
+  resized_matrix.resizeLike(matrix);
+#ifdef EIGEN_NO_STATIC_ASSERT
+  VERIFY_RAISES_ASSERT(EIGEN_INTERNAL_ASSERT_SCALAR(matrix));
+#endif
+  VERIFY_IS_EQUAL(ref.size(), 3);
+  MatrixXd dynamic_matrix = matrix;
+  VERIFY_RAISES_ASSERT((Ref<VectorXd>(dynamic_matrix)));
+  VERIFY_IS_EQUAL(matrix.determinant(), 1);
+  VERIFY_IS_EQUAL(matrix.householderQr().absDeterminant(), 1);
+  VERIFY_IS_EQUAL(matrix.fullPivLu().determinant(), 1);
+  VERIFY_IS_EQUAL(matrix.fullPivLu().rcond(), 1);
+  Matrix3d transpose_source = Matrix3d::Random();
+  const Matrix3d transposed = transpose_source.transpose();
+  transpose_source.transposeInPlace();
+  VERIFY_IS_EQUAL(transpose_source, transposed);
+  MatrixXd rectangular_transpose = MatrixXd::Random(2, 3);
+  const MatrixXd rectangular_expected = rectangular_transpose.transpose();
+  rectangular_transpose.transposeInPlace();
+  VERIFY_IS_EQUAL(rectangular_transpose, rectangular_expected);
+  VERIFY(matrix.llt().info() == Success);
+  Vector2d rhs = Vector2d::Ones();
+  const auto lu = matrix.fullPivLu();
+  VERIFY_IS_EQUAL(lu.solve(rhs).eval(), rhs);
+  VERIFY_IS_EQUAL(lu.transpose().solve(rhs).eval(), rhs);
+  Matrix2d nonsymmetric;
+  nonsymmetric << 2, 1, 0, 3;
+  const auto partial_lu = nonsymmetric.partialPivLu();
+  const Vector2d transpose_solution = partial_lu.transpose().solve(rhs);
+  VERIFY_IS_APPROX(nonsymmetric.transpose() * transpose_solution, rhs);
+  VERIFY_RAISES_ASSERT(partial_lu.transpose().solve(VectorXd::Ones(3)));
+  VERIFY_RAISES_ASSERT(lu.solve(VectorXd::Ones(3)));
+#ifdef EIGEN_NO_STATIC_ASSERT
+  VERIFY_RAISES_ASSERT(lu.solve(Vector3d::Ones()));
+#endif
+  VERIFY_IS_APPROX(matrix.sqrt(), matrix);
+  VERIFY(SelfAdjointEigenSolver<Matrix2d>(matrix).info() == Success);
+  MatrixXd rectangle = MatrixXd::Ones(2, 3);
+  VERIFY_RAISES_ASSERT(rectangle.determinant());
+  VERIFY_RAISES_ASSERT(rectangle.householderQr().absDeterminant());
+  VERIFY_RAISES_ASSERT(rectangle.fullPivLu().determinant());
+  VERIFY_RAISES_ASSERT(rectangle.llt());
+  VERIFY_RAISES_ASSERT(wide.fullPivLu().determinant());
+  MatrixXd empty(0, 0);
+  VERIFY_IS_EQUAL(empty.sum(), 0);
+  const Matrix<double, 0, 0> fixed_empty;
+  VERIFY_IS_EQUAL(fixed_empty.sum(), 0);
+  VERIFY_IS_EQUAL(fixed_empty.prod(), 1);
+  Matrix<double, 3, 0> empty_columns;
+  Matrix<double, 0, 3> empty_rows;
+  VERIFY_IS_EQUAL(empty_columns.colwise().maxCoeff().eval().size(), 0);
+  VERIFY_IS_EQUAL(empty_rows.rowwise().minCoeff().eval().size(), 0);
+  VERIFY_RAISES_ASSERT(fixed_empty.maxCoeff());
+  VERIFY_RAISES_ASSERT(empty_columns.rowwise().maxCoeff());
+  VERIFY_RAISES_ASSERT(empty_rows.colwise().minCoeff());
+  VERIFY_RAISES_ASSERT(empty.maxCoeff());
+  VERIFY_IS_EQUAL(matrix.maxCoeff(), 1);
+  Index max_row, max_col;
+  VERIFY_IS_EQUAL(matrix.maxCoeff(&max_row, &max_col), 1);
+  CALL_SUBTEST(product_shape_checks());
+  CALL_SUBTEST((product_destination_shape_checks<3, ColMajor>()));
+  CALL_SUBTEST((product_destination_shape_checks<3, RowMajor>()));
+  CALL_SUBTEST((product_destination_shape_checks<Dynamic, ColMajor>()));
+  CALL_SUBTEST((product_destination_shape_checks<Dynamic, RowMajor>()));
+  using namespace Eigen::internal;
+  STATIC_CHECK((dimension_relation<3, 3>::Proven));
+  STATIC_CHECK((!dimension_relation<3, 2>::Compatible));
+  STATIC_CHECK((dimension_relation<Dynamic, 3>::Compatible));
+  STATIC_CHECK((!dimension_relation<Dynamic, 3>::Known));
+  STATIC_CHECK((compile_time_shape<MissingShape>::Rows == Dynamic));
+  STATIC_CHECK((compile_time_shape<PartialShape>::Rows == 3));
+  STATIC_CHECK((compile_time_shape<PartialShape>::Cols == Dynamic));
+  STATIC_CHECK((compile_time_shape<Matrix<double, Dynamic, 1, 0, 3, 1>>::Size == Dynamic));
+  STATIC_CHECK((matching_sizes<Matrix<double, 2, 3>, Matrix<double, 3, 2>>::Proven));
+  STATIC_CHECK((matching_shapes<Matrix<double, 1, 0>, Matrix<double, 0, 1>>::Compatible));
+  const Matrix<double, 1, 0> empty_row;
+  const Matrix<double, 0, 1> empty_column;
+  VERIFY_RAISES_ASSERT(empty_row + empty_column);
+  VERIFY_RAISES_ASSERT(internal::resize_if_allowed(empty_row, empty_column, internal::add_assign_op<double, double>()));
+  STATIC_CHECK((square_dimensions<const Matrix3d&>::Proven));
+  int reads = 0;
+  struct RuntimeDimensions {
+    mutable int accesses = 0;
+    Index rows() const {
+      ++accesses;
+      return 2;
+    }
+    Index cols() const {
+      ++accesses;
+      return 3;
+    }
+  } runtime_a, runtime_b;
+  EIGEN_INTERNAL_ASSERT_SAME_ROWS(runtime_a, runtime_b);
+  EIGEN_INTERNAL_ASSERT_SAME_COLS(runtime_a, runtime_b);
+#ifndef EIGEN_NO_DEBUG
+  VERIFY_IS_EQUAL(runtime_a.accesses, 2);
+  VERIFY_IS_EQUAL(runtime_b.accesses, 2);
+#endif
+  EIGEN_INTERNAL_SHAPE_ASSERT((dimension_relation<3, 3>::Proven), ++reads == 1);  // NOLINT(bugprone-assert-side-effect)
+  VERIFY_IS_EQUAL(reads, 0);
+  EIGEN_INTERNAL_ASSERT_NONEMPTY_SIZE(fixed_a, ++reads);  // NOLINT(bugprone-assert-side-effect)
+  VERIFY_IS_EQUAL(reads, 0);
+#ifndef EIGEN_NO_DEBUG
+  // NOLINTBEGIN(bugprone-assert-side-effect)
+  EIGEN_INTERNAL_SHAPE_ASSERT((dimension_relation<Dynamic, 3>::Proven), ++reads == 1);
+  // NOLINTEND(bugprone-assert-side-effect)
+  VERIFY_IS_EQUAL(reads, 1);
+  VERIFY_RAISES_ASSERT([] { EIGEN_INTERNAL_SHAPE_ASSERT(false, false); }());
+#endif
+}
diff --git a/test/shape_checks_compatibility.cpp b/test/shape_checks_compatibility.cpp
new file mode 100644
index 0000000..4f3ff4e
--- /dev/null
+++ b/test/shape_checks_compatibility.cpp
@@ -0,0 +1,87 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#include "main.h"
+#include <Eigen/LU>
+#include <Eigen/QR>
+
+struct RuntimeShape;
+namespace Eigen {
+namespace internal {
+template <>
+struct traits<RuntimeShape> {
+  using StorageKind = Dense;
+};
+}  // namespace internal
+}  // namespace Eigen
+
+struct RuntimeShape : EigenBase<RuntimeShape> {
+  mutable int reads = 0;
+  Index rows() const {
+    ++reads;
+    return 2;
+  }
+  Index cols() const {
+    ++reads;
+    return 1;
+  }
+};
+
+template <int N>
+double guarded_tail_max(const Matrix<double, N, 1>& vector) {
+  const auto tail = vector.template tail<N - 1>();
+  return tail.size() > 0 ? tail.maxCoeff() : 0.0;
+}
+
+template <int N>
+Index guarded_column_max_size(const Matrix<double, N, N>& matrix) {
+  return matrix.cols() > 0 ? matrix.colwise().maxCoeff().size() : 0;
+}
+
+template <int Rows, int Cols>
+double guarded_determinants(const Matrix<double, Rows, Cols>& matrix) {
+  if (matrix.rows() != matrix.cols()) return 0.0;
+  return matrix.determinant() + matrix.fullPivLu().determinant() + matrix.householderQr().absDeterminant() +
+         matrix.colPivHouseholderQr().absDeterminant() + matrix.fullPivHouseholderQr().absDeterminant() +
+         matrix.randColPivHouseholderQr().absDeterminant();
+}
+
+EIGEN_DECLARE_TEST(shape_checks_compatibility) {
+  VERIFY_IS_EQUAL(guarded_tail_max<1>(Matrix<double, 1, 1>::Ones()), 0.0);
+  VERIFY_IS_EQUAL(guarded_tail_max<2>(Vector2d::Constant(7)), 7.0);
+  VERIFY_IS_EQUAL(guarded_column_max_size(Matrix<double, 0, 0>()), Index(0));
+  VERIFY_IS_EQUAL(guarded_column_max_size<2>(Matrix2d::Ones()), Index(2));
+  VERIFY_IS_EQUAL((guarded_determinants<3, 4>(Matrix<double, 3, 4>::Zero())), 0.0);
+  VERIFY_IS_EQUAL((guarded_determinants<2, 2>(Matrix2d::Identity())), 6.0);
+  Matrix<double, 0, 4> empty_rectangle;
+  if (empty_rectangle.rows() == empty_rectangle.cols()) empty_rectangle.transposeInPlace();
+  VERIFY_RAISES_ASSERT(empty_rectangle.transposeInPlace());
+  RuntimeShape source;
+  VectorXd destination;
+  destination.resizeLike(source);
+  VERIFY_IS_EQUAL(destination.size(), 2);
+  internal::resize_if_allowed(destination, source, internal::add_assign_op<double, double>());
+  const EigenBase<RuntimeShape>& base = source;
+  source.reads = 0;
+  EIGEN_INTERNAL_ASSERT_SAME_SHAPE(base, destination);
+#ifndef EIGEN_NO_DEBUG
+  VERIFY_IS_EQUAL(source.reads, 2);
+#elif defined(EIGEN_NO_ASSERTION_CHECKING)
+  VERIFY_IS_EQUAL(source.reads, 0);
+#endif
+  VERIFY_RAISES_ASSERT(EIGEN_INTERNAL_ASSERT_SAME_ROWS(base, Vector3d::Zero()));
+
+  using BoundedVector = Matrix<double, Dynamic, 1, 0, 4, 1>;
+  const BoundedVector a = BoundedVector::Ones(3);
+  const BoundedVector b = BoundedVector::Ones(4);
+  VERIFY_RAISES_ASSERT(a.dot(b));
+  VERIFY_IS_EQUAL(a.dot(a), 3);
+  Matrix<double, Dynamic, Dynamic, 0, 4, 4> bounded_matrix = Matrix3d::Identity();
+  VERIFY_RAISES_ASSERT(bounded_matrix * b);
+  VERIFY_IS_EQUAL((bounded_matrix * a).eval(), a);
+
+  const Matrix2d fixed = Matrix2d::Identity();
+  Matrix<double, 2, Dynamic> wrong_cols(2, 3);
+  Matrix<double, Dynamic, 2> wrong_rows(3, 2);
+  VERIFY_RAISES_ASSERT(fixed + wrong_cols);
+  VERIFY_RAISES_ASSERT(fixed + wrong_rows);
+}
diff --git a/test/shape_checks_modules.cpp b/test/shape_checks_modules.cpp
new file mode 100644
index 0000000..2cb7a8b
--- /dev/null
+++ b/test/shape_checks_modules.cpp
@@ -0,0 +1,141 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+
+#include "main.h"
+
+#if defined(EIGEN_TEST_PART_1)
+#include <Eigen/Eigenvalues>
+#include <contrib/Eigen/MatrixFunctions>
+
+void dense_module_shapes() {
+  const Matrix2d a = 2 * Matrix2d::Identity();
+  const Vector2d v = Vector2d::Ones();
+  LLT<Matrix2d> llt(a);
+  LDLT<Matrix2d> ldlt(a);
+  llt.rankUpdate(v);
+  ldlt.rankUpdate(v);
+  VERIFY_IS_APPROX(llt.reconstructedMatrix(), a + v * v.transpose());
+  VERIFY_IS_APPROX(ldlt.reconstructedMatrix(), a + v * v.transpose());
+  VectorXd wrong = VectorXd::Ones(3);
+  VERIFY_RAISES_ASSERT(llt.rankUpdate(wrong));
+  VERIFY_RAISES_ASSERT(ldlt.rankUpdate(wrong));
+  SelfAdjointEigenSolver<Matrix2d> direct;
+  direct.computeDirect(a);
+  VERIFY_IS_EQUAL(direct.info(), Success);
+  VERIFY_IS_EQUAL(direct.eigenvalues(), Vector2d::Constant(2));
+  RealSchur<Matrix2d> schur(a);
+  VERIFY_IS_EQUAL(schur.info(), Success);
+  RealQZ<Matrix2d> real_qz(a, a);
+  VERIFY_IS_EQUAL(real_qz.info(), Success);
+  ComplexQZ<Matrix2cd> complex_qz(a.cast<std::complex<double>>(), a.cast<std::complex<double>>());
+  VERIFY_IS_EQUAL(complex_qz.info(), Success);
+  GeneralizedSelfAdjointEigenSolver<Matrix2d> generalized(a, a);
+  VERIFY_IS_EQUAL(generalized.info(), Success);
+  const Matrix2d solution = internal::matrix_function_solve_triangular_sylvester(a, a, a);
+  VERIFY_IS_APPROX(a * solution + solution * a, a);
+  MatrixXd rectangular = MatrixXd::Ones(2, 3);
+  MatrixXd dynamic_a = a;
+  VERIFY_RAISES_ASSERT(internal::matrix_function_solve_triangular_sylvester(dynamic_a, dynamic_a, rectangular));
+  Vector2d coeffs;
+  VERIFY_RAISES_ASSERT(internal::tridiagonalization_inplace_unblocked(rectangular, coeffs));
+}
+#endif
+
+#if defined(EIGEN_TEST_PART_2)
+#include <Eigen/Sparse>
+#include <Eigen/Eigenvalues>
+
+void sparse_module_shapes() {
+  SparseMatrix<double> a(3, 3);
+  a.setIdentity();
+  SimplicialLLT<SparseMatrix<double>> llt(a);
+  SimplicialLDLT<SparseMatrix<double>> ldlt(a);
+  SparseLU<SparseMatrix<double>> lu(a);
+  SparseQR<SparseMatrix<double>, COLAMDOrdering<int>> qr(a);
+  const VectorXd rhs = VectorXd::Ones(3);
+  VERIFY_IS_APPROX(llt.solve(rhs), rhs);
+  VERIFY_IS_APPROX(ldlt.solve(rhs), rhs);
+  VERIFY_IS_APPROX(lu.solve(rhs), rhs);
+  MatrixXd q = qr.matrixQ();
+  VERIFY_IS_APPROX((qr.matrixQ() * rhs).eval(), q * rhs);
+  VERIFY_IS_APPROX((qr.matrixQ().adjoint() * rhs).eval(), q.adjoint() * rhs);
+  SparseMatrix<double> rectangular(2, 3);
+  VERIFY_RAISES_ASSERT(rectangular.selfadjointView<Lower>());
+  VERIFY_RAISES_ASSERT(llt.compute(rectangular));
+  VERIFY_RAISES_ASSERT(ldlt.compute(rectangular));
+  VERIFY_RAISES_ASSERT(lu.compute(rectangular));
+  SparseVector<double> empty(0);
+  VectorXd empty_dense(0);
+  VERIFY_IS_EQUAL(empty.dot(empty_dense), 0.0);
+}
+#endif
+
+#if defined(EIGEN_TEST_PART_3)
+#include <contrib/Eigen/StructuredMatrices>
+
+void structured_module_shapes() {
+  Vector2d x(1, 2), y(4, 5);
+  const Cauchy<double, 2, 2> c(x, y);
+  VERIFY_IS_APPROX(c.determinant(), Matrix2d(c).determinant());
+  const Vandermonde<double, 2, 2> v(x, 2);
+  VERIFY_IS_APPROX(v.determinant(), Matrix2d(v).determinant());
+  const Matrix2d identity = Matrix2d::Identity();
+  const Matrix<double, 1, 1> one = Matrix<double, 1, 1>::Ones();
+  const KroneckerOperator<Matrix2d, Matrix<double, 1, 1>> kron(identity, one);
+  VERIFY_IS_EQUAL(kron.determinant(), 1);
+  VERIFY_IS_APPROX(kron.solve(x), x);
+  VERIFY_IS_APPROX(Matrix2d(kron.inverse()), identity);
+  VERIFY_IS_APPROX(kron.eigenvalues(), Vector2cd::Ones());
+  VERIFY_IS_EQUAL(kron.eigenvectors().rows(), 2);
+  VectorXd three = VectorXd::LinSpaced(3, 4, 6);
+  const Cauchy<double> rectangular_c(x, three);
+  const Vandermonde<double> rectangular_v(x, 3);
+  VERIFY_RAISES_ASSERT(rectangular_c.determinant());
+  VERIFY_RAISES_ASSERT(rectangular_v.determinant());
+  CauchyLU<double> clu;
+  BjorckPereyra<double> bp;
+  VERIFY_RAISES_ASSERT(clu.compute(rectangular_c));
+  VERIFY_RAISES_ASSERT(bp.compute(rectangular_v));
+  const Hankel<double> hankel(x, three);
+  VERIFY_RAISES_ASSERT(hankel.solve(x));
+  const Toeplitz<double> toeplitz(x, three);
+  LookAheadLevinson<double> levinson;
+  VERIFY_RAISES_ASSERT(levinson.compute(toeplitz));
+  MatrixXd tall = MatrixXd::Ones(2, 1), wide = MatrixXd::Ones(1, 2);
+  const KroneckerOperator<MatrixXd, MatrixXd> rectangular_factors(tall, wide);
+  VERIFY_IS_EQUAL(rectangular_factors.rows(), rectangular_factors.cols());
+  VERIFY_RAISES_ASSERT(rectangular_factors.determinant());
+  VERIFY_RAISES_ASSERT(rectangular_factors.inverse());
+  VERIFY_RAISES_ASSERT(rectangular_factors.eigenvalues());
+  VERIFY_RAISES_ASSERT(rectangular_factors.eigenvectors());
+  VERIFY_RAISES_ASSERT(rectangular_factors.solve(x));
+  const Cauchy<double, 2, 3> fixed_rectangular(x, three);
+  VERIFY_RAISES_ASSERT(fixed_rectangular.determinant());
+}
+#endif
+
+#if defined(EIGEN_TEST_PART_4)
+#include <contrib/Eigen/FFT>
+
+void fft_shapes() {
+  FFT<double> fft;
+  Vector2d input(1, 2), recovered;
+  Vector2cd spectrum;
+  fft.fwd(spectrum, input);
+  fft.inv(recovered, spectrum);
+  VERIFY_IS_APPROX(recovered, input);
+  MatrixXd not_vector = MatrixXd::Ones(2, 2);
+  VectorXcd output;
+  VERIFY_RAISES_ASSERT(fft.fwd(output, not_vector));
+  MatrixXcd complex_not_vector = MatrixXcd::Ones(2, 2);
+  VectorXd real_output;
+  VERIFY_RAISES_ASSERT(fft.inv(real_output, complex_not_vector));
+}
+#endif
+
+EIGEN_DECLARE_TEST(shape_checks_modules) {
+  CALL_SUBTEST_1(dense_module_shapes());
+  CALL_SUBTEST_2(sparse_module_shapes());
+  CALL_SUBTEST_3(structured_module_shapes());
+  CALL_SUBTEST_4(fft_shapes());
+}
diff --git a/test/shape_checks_modules_no_static_assert.cpp b/test/shape_checks_modules_no_static_assert.cpp
new file mode 100644
index 0000000..43273b7
--- /dev/null
+++ b/test/shape_checks_modules_no_static_assert.cpp
@@ -0,0 +1,5 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+// EIGEN_SUFFIXES;1;2;3;4
+#define EIGEN_NO_STATIC_ASSERT
+#include "shape_checks_modules.cpp"  // NOLINT(bugprone-suspicious-include): Reuse the suite with static assertions disabled.
diff --git a/test/shape_checks_no_static_assert.cpp b/test/shape_checks_no_static_assert.cpp
new file mode 100644
index 0000000..11bee4c
--- /dev/null
+++ b/test/shape_checks_no_static_assert.cpp
@@ -0,0 +1,4 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#define EIGEN_NO_STATIC_ASSERT
+#include "shape_checks.cpp"  // NOLINT(bugprone-suspicious-include): Reuse the suite with static assertions disabled.
diff --git a/test/shape_size_axis.cpp b/test/shape_size_axis.cpp
new file mode 100644
index 0000000..7c14e8b
--- /dev/null
+++ b/test/shape_size_axis.cpp
@@ -0,0 +1,83 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+
+#include "main.h"
+
+template <int Rows, int Cols>
+struct AxisShapeProbe;
+namespace Eigen {
+namespace internal {
+template <int Rows, int Cols>
+struct traits<AxisShapeProbe<Rows, Cols>> {
+  static constexpr int RowsAtCompileTime = Rows;
+  static constexpr int ColsAtCompileTime = Cols;
+};
+}  // namespace internal
+}  // namespace Eigen
+
+template <int Rows, int Cols>
+struct AxisShapeProbe {
+  Index row_count, col_count;
+  mutable int size_reads = 0, row_reads = 0, col_reads = 0;
+  Index size() const {
+    ++size_reads;
+    return row_count * col_count;
+  }
+  Index rows() const {
+    ++row_reads;
+    return row_count;
+  }
+  Index cols() const {
+    ++col_reads;
+    return col_count;
+  }
+};
+
+struct UnknownAxisShape {
+  Index size() const { return 3; }
+  Index rows() const { return 3; }
+  Index cols() const { return 4; }
+};
+
+EIGEN_DECLARE_TEST(shape_size_axis) {
+  using namespace Eigen::internal;
+  STATIC_CHECK((size_matches_axis<RowVector3d, Matrix<double, 3, 4>, true>::Proven));
+  STATIC_CHECK((size_matches_axis<Vector4d, Matrix<double, 3, 4>, false>::Proven));
+  STATIC_CHECK((!size_matches_axis<Vector3d, Matrix<double, 3, 4>, false>::Compatible));
+  STATIC_CHECK((!size_matches_axis<Matrix<double, Dynamic, 1, 0, 4, 1>, Matrix4d, true>::Known));
+  STATIC_CHECK((!size_matches_axis<UnknownAxisShape, Matrix3d, true>::Known));
+
+  AxisShapeProbe<1, 3> row_vector{1, 3};
+  AxisShapeProbe<4, 1> col_vector{4, 1};
+  AxisShapeProbe<3, 4> fixed{3, 4};
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(row_vector, fixed);
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS(col_vector, fixed);
+  VERIFY_IS_EQUAL(row_vector.size_reads + col_vector.size_reads + fixed.row_reads + fixed.col_reads, 0);
+
+  AxisShapeProbe<Dynamic, 4> partial{3, 4};
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS(col_vector, partial);
+  VERIFY_IS_EQUAL(col_vector.size_reads + partial.col_reads, 0);
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(row_vector, partial);
+#ifndef EIGEN_NO_DEBUG
+  VERIFY_IS_EQUAL(row_vector.size_reads, 1);
+  VERIFY_IS_EQUAL(partial.row_reads, 1);
+#endif
+  VERIFY_IS_EQUAL(partial.col_reads, 0);
+
+  const UnknownAxisShape unknown;
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(unknown, fixed);
+  VERIFY_RAISES_ASSERT(EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS(unknown, fixed));
+  AxisShapeProbe<Dynamic, Dynamic> wrong{2, 4};
+  VERIFY_RAISES_ASSERT(EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(row_vector, wrong));
+#ifdef EIGEN_NO_STATIC_ASSERT
+  VERIFY_RAISES_ASSERT(EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_COLS(row_vector, fixed));
+#endif
+  const Matrix<double, 0, 4> empty;
+  const Matrix<double, 0, 1> empty_vector;
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(empty_vector, empty);
+  const Matrix<double, 3, 4> matrix = Matrix<double, 3, 4>::Zero();
+  const Vector3d vector = Vector3d::Ones();
+  const MatrixBase<Vector3d>& vector_base = vector;
+  const MatrixBase<Matrix<double, 3, 4>>& matrix_base = matrix;
+  EIGEN_INTERNAL_ASSERT_SIZE_MATCHES_ROWS(vector_base, matrix_base);
+}
diff --git a/test/shape_size_axis_applications.cpp b/test/shape_size_axis_applications.cpp
new file mode 100644
index 0000000..3e50202
--- /dev/null
+++ b/test/shape_size_axis_applications.cpp
@@ -0,0 +1,66 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+
+#include "main.h"
+#include <Eigen/Dense>
+#include <Eigen/Sparse>
+#include <contrib/Eigen/StructuredMatrices>
+
+template <int Order>
+void axis_product_checks() {
+  using Rect = Matrix<double, 3, 4, Order>;
+  const Rect matrix = Rect::Random();
+  const Vector3d left = Vector3d::Constant(2);
+  const Vector4d right = Vector4d::Constant(3);
+  Rect result = left.asDiagonal() * matrix.template triangularView<Upper>();
+  VERIFY_IS_APPROX(result, 2 * matrix.template triangularView<Upper>().toDenseMatrix());
+  result = matrix.template triangularView<Lower>() * right.asDiagonal();
+  VERIFY_IS_APPROX(result, 3 * matrix.template triangularView<Lower>().toDenseMatrix());
+  const Matrix3d identity = Matrix3d::Identity();
+  Matrix3d square = identity.template selfadjointView<Lower>() * left.asDiagonal();
+  VERIFY_IS_EQUAL(square, 2 * identity);
+  const VectorXd wrong = VectorXd::Ones(2);
+  VERIFY_RAISES_ASSERT(result.noalias() = wrong.asDiagonal() * matrix.template triangularView<Upper>());
+  VERIFY_RAISES_ASSERT(result.noalias() = matrix.template triangularView<Lower>() * wrong.asDiagonal());
+}
+
+EIGEN_DECLARE_TEST(shape_size_axis_applications) {
+  CALL_SUBTEST((axis_product_checks<ColMajor>()));
+  CALL_SUBTEST((axis_product_checks<RowMajor>()));
+  const Matrix3d identity = Matrix3d::Identity();
+  const Vector3d vector = Vector3d::Ones();
+  const VectorXd wrong = VectorXd::Ones(2);
+  LLT<Matrix3d> llt(identity);
+  llt.rankUpdate(vector);
+  VERIFY_IS_APPROX(llt.reconstructedMatrix(), identity + vector * vector.transpose());
+  VERIFY_RAISES_ASSERT(llt.rankUpdate(wrong));
+  LDLT<Matrix3d> ldlt(identity);
+  ldlt.rankUpdate(vector);
+  VERIFY_IS_APPROX(ldlt.reconstructedMatrix(), identity + vector * vector.transpose());
+  VERIFY_RAISES_ASSERT(ldlt.rankUpdate(wrong));
+  VERIFY_IS_APPROX(identity.partialPivLu().solve(vector), vector);
+  SelfAdjointEigenSolver<Matrix3d> eig(identity);
+  VERIFY_IS_EQUAL(eig.eigenvalues(), vector);
+
+  Matrix<double, 3, 2> qr_matrix = Matrix<double, 3, 2>::Zero();
+  Vector2d coefficients;
+  double workspace[2];
+  const Vector3d column = Vector3d::UnitX();
+  internal::householder_qr_inplace_update(qr_matrix, coefficients, column, 0, workspace);
+  VERIFY_RAISES_ASSERT(internal::householder_qr_inplace_update(qr_matrix, coefficients, wrong, 1, workspace));
+  const Vector3d column2 = Vector3d::UnitY();
+  internal::householder_qr_inplace_update(qr_matrix, coefficients, column2, 1, workspace);
+  const Matrix3d q = householderSequence(qr_matrix, coefficients);
+  VERIFY_IS_APPROX(q * qr_matrix.triangularView<Upper>().toDenseMatrix(), identity.leftCols<2>());
+
+  const SparseMatrix<double> sparse_identity = identity.sparseView();
+  DiagonalPreconditioner<double> preconditioner(sparse_identity);
+  VERIFY_IS_APPROX(preconditioner.solve(vector), vector);
+  VERIFY_RAISES_ASSERT(preconditioner.solve(wrong));
+  const MatrixXd factor = MatrixXd::Ones(3, 1);
+  DiagonalPlusLowRank<double> lowrank(vector, factor, factor);
+  VERIFY_IS_APPROX((lowrank * vector).eval(), (identity + factor * factor.transpose()) * vector);
+  const MatrixXd bad_factor = MatrixXd::Ones(2, 1);
+  VERIFY_RAISES_ASSERT(DiagonalPlusLowRank<double>(vector, bad_factor, factor));
+  VERIFY_RAISES_ASSERT(DiagonalPlusLowRank<double>(vector, factor, bad_factor));
+}
diff --git a/test/shape_size_axis_no_static_assert.cpp b/test/shape_size_axis_no_static_assert.cpp
new file mode 100644
index 0000000..2a533ae
--- /dev/null
+++ b/test/shape_size_axis_no_static_assert.cpp
@@ -0,0 +1,4 @@
+// SPDX-FileCopyrightText: The Eigen Authors
+// SPDX-License-Identifier: MPL-2.0
+#define EIGEN_NO_STATIC_ASSERT
+#include "shape_size_axis.cpp"  // NOLINT(bugprone-suspicious-include): Reuse the suite with static assertions disabled.
diff --git a/test/sparse_threaded_product.cpp b/test/sparse_threaded_product.cpp
index ecc1de9..f63ce2b 100644
--- a/test/sparse_threaded_product.cpp
+++ b/test/sparse_threaded_product.cpp
@@ -33,6 +33,13 @@
   DenseVectorType y_adj_ref = refMat.adjoint() * x_adj;
 
   ThreadedSparseProduct<SparseMatrixType> op(A);
+  {
+    DenseVectorType wrong_x = DenseVectorType::Zero(cols + 1);
+    DenseVectorType wrong_adjoint_x = DenseVectorType::Zero(rows + 1);
+    DenseVectorType y(rows), adjoint_y(cols);
+    VERIFY_RAISES_ASSERT(op.apply(wrong_x, y));
+    VERIFY_RAISES_ASSERT(op.applyAdjoint(wrong_adjoint_x, adjoint_y));
+  }
   VERIFY(op.rows() == A.rows());
   VERIFY(op.cols() == A.cols());
 
diff --git a/test/svd_common.h b/test/svd_common.h
index f76cd35..f1e6a6f 100644
--- a/test/svd_common.h
+++ b/test/svd_common.h
@@ -551,13 +551,17 @@
   RhsType rhs = RhsType::Zero(m.rows());
   EIGEN_UNUSED_VARIABLE(rhs);  // Only used if asserts are enabled.
 
+  using TransposeRhsType = Matrix<typename MatrixType::Scalar, MatrixType::ColsAtCompileTime, 1>;
+  TransposeRhsType transposeRhs = TransposeRhsType::Zero(m.cols());
+  EIGEN_UNUSED_VARIABLE(transposeRhs);
+
   SVD_STATIC_OPTIONS(MatrixType, QRPreconditioner) svd0;
   VERIFY_RAISES_ASSERT((svd0.matrixU()));
   VERIFY_RAISES_ASSERT((svd0.singularValues()));
   VERIFY_RAISES_ASSERT((svd0.matrixV()));
   VERIFY_RAISES_ASSERT((svd0.solve(rhs)));
-  VERIFY_RAISES_ASSERT((svd0.transpose().solve(rhs)));
-  VERIFY_RAISES_ASSERT((svd0.adjoint().solve(rhs)));
+  VERIFY_RAISES_ASSERT((svd0.transpose().solve(transposeRhs)));
+  VERIFY_RAISES_ASSERT((svd0.adjoint().solve(transposeRhs)));
 
   SVD_STATIC_OPTIONS(MatrixType, QRPreconditioner) svd1(m);
   VERIFY_RAISES_ASSERT((svd1.matrixU()));
diff --git a/test/zerosized.cpp b/test/zerosized.cpp
index 7393a36..7facccd 100644
--- a/test/zerosized.cpp
+++ b/test/zerosized.cpp
@@ -55,7 +55,9 @@
   fixed << Scalar(2), Scalar(3);
   dynamic = fixed;
   VERIFY_IS_EQUAL(double(internal::redux_with_identity(fixed, sum, identity)), 5.0);
-  VERIFY_IS_EQUAL(double(internal::redux_with_identity(dynamic, sum, std::move(identity))), 5.0);
+  VERIFY_IS_EQUAL(double(internal::redux_with_identity(
+                      dynamic, sum, ReductionIdentity<Scalar>(runtimeIdentity, lvalueConversions, rvalueConversions))),
+                  5.0);
   VERIFY_IS_EQUAL(lvalueConversions, 3);
   VERIFY_IS_EQUAL(rvalueConversions, 0);