MSVC: Fix the MSVC 14.29 build of nine tests

libeigen/eigen!3369

Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
diff --git a/Eigen/src/Core/products/GeneralMatrixMatrix.h b/Eigen/src/Core/products/GeneralMatrixMatrix.h
index 842c37e..529f7d0 100644
--- a/Eigen/src/Core/products/GeneralMatrixMatrix.h
+++ b/Eigen/src/Core/products/GeneralMatrixMatrix.h
@@ -408,7 +408,10 @@
   };
   using LhsScalar = std::conditional_t<Transpose, RhsScalar_, LhsScalar_>;
   using RhsScalar = std::conditional_t<Transpose, LhsScalar_, RhsScalar_>;
-  enum { SizeA = ActualRows * MaxDepth, SizeB = ActualCols * MaxDepth };
+  // A zero-sized fixed dimension must not declare a zero-length array, which MSVC rejects (C2229); every caller
+  // returns before it touches the blocks of an empty product.
+  static constexpr int SizeA = plain_enum_max(int(ActualRows) * MaxDepth, 1);
+  static constexpr int SizeB = plain_enum_max(int(ActualCols) * MaxDepth, 1);
 
 #if EIGEN_MAX_STATIC_ALIGN_BYTES >= EIGEN_DEFAULT_ALIGN_BYTES
   EIGEN_ALIGN_MAX LhsScalar m_staticA[SizeA];
diff --git a/contrib/Eigen/src/Tensor/TensorEvaluator.h b/contrib/Eigen/src/Tensor/TensorEvaluator.h
index 84decf9..fc3687e 100644
--- a/contrib/Eigen/src/Tensor/TensorEvaluator.h
+++ b/contrib/Eigen/src/Tensor/TensorEvaluator.h
@@ -989,6 +989,13 @@
   static constexpr bool VectorizedCondCast =
       std::is_same<CondScalar, bool>::value && TensorEvaluator<IfArgType, Device>::PacketAccess &&
       internal::type_casting_traits<CondScalar, std::remove_const_t<Scalar>>::VectorizedCast;
+  // How condPacket() builds the mask: 0 = coefficient by coefficient, 1 = from one condition packet, 2 = from two,
+  // 3 = from one wider condition packet while it stays inside the condition.
+  static constexpr int CondPacketMode = !VectorizedCondCast                ? 0
+                                        : CondPacketSize == PacketSize     ? 1
+                                        : PacketSize == 2 * CondPacketSize ? 2
+                                        : CondPacketSize > PacketSize      ? 3
+                                                                           : 0;
   typedef typename TensorEvaluator<IfArgType, Device>::Dimensions Dimensions;
   typedef StorageMemory<CoeffReturnType, Device> Storage;
   typedef typename Storage::Type EvaluatorPointerType;
@@ -1054,36 +1061,36 @@
     return m_condImpl.coeff(index) ? m_thenImpl.coeff(index) : m_elseImpl.coeff(index);
   }
 
-  template <bool UseVecCast = VectorizedCondCast>
-  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::enable_if_t<UseVecCast && (CondPacketSize == PacketSize), PacketReturnType>
-  condPacket(Index index) const {
+  // Tag dispatch rather than enable_if overloads: MSVC 19.29 folds the non-dependent part of each enable_if
+  // condition and then rejects the overloads whose conditions fold alike as redeclarations (C2535). Modes 1-3 are
+  // member templates so that explicitly instantiating the evaluator does not instantiate pcasts its packet types lack.
+  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType condPacket(Index index) const {
+    return condPacket(index, std::integral_constant<int, CondPacketMode>());
+  }
+
+  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType condPacket(Index index, std::integral_constant<int, 0>) const {
+    return scalarCondPacket(index);
+  }
+
+  template <typename Unused = void>
+  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType condPacket(Index index, std::integral_constant<int, 1>) const {
     return internal::pcast<CondPacket, PacketReturnType>(m_condImpl.template packet<Unaligned>(index));
   }
 
-  template <bool UseVecCast = VectorizedCondCast>
-  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE
-      std::enable_if_t<UseVecCast && (PacketSize == 2 * CondPacketSize), PacketReturnType>
-      condPacket(Index index) const {
+  template <typename Unused = void>
+  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType condPacket(Index index, std::integral_constant<int, 2>) const {
     return internal::pcast<CondPacket, PacketReturnType>(m_condImpl.template packet<Unaligned>(index),
                                                          m_condImpl.template packet<Unaligned>(index + CondPacketSize));
   }
 
-  template <bool UseVecCast = VectorizedCondCast>
-  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::enable_if_t<UseVecCast && (CondPacketSize > PacketSize), PacketReturnType>
-  condPacket(Index index) const {
+  template <typename Unused = void>
+  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType condPacket(Index index, std::integral_constant<int, 3>) const {
     if (index + CondPacketSize <= m_condSize) {
       return internal::pcast<CondPacket, PacketReturnType>(m_condImpl.template packet<Unaligned>(index));
     }
     return scalarCondPacket(index);
   }
 
-  template <bool UseVecCast = VectorizedCondCast>
-  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::enable_if_t<
-      !UseVecCast || (CondPacketSize < PacketSize && PacketSize != 2 * CondPacketSize), PacketReturnType>
-  condPacket(Index index) const {
-    return scalarCondPacket(index);
-  }
-
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType scalarCondPacket(Index index) const {
     EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
     std::remove_const_t<Scalar> arr[PacketSize];
@@ -1108,9 +1115,7 @@
   }
 
   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
-    const bool cond_vectorized =
-        vectorized && (TernaryPacketAccess ||
-                       (VectorizedCondCast && (CondPacketSize >= PacketSize || PacketSize == 2 * CondPacketSize)));
+    const bool cond_vectorized = vectorized && (TernaryPacketAccess || CondPacketMode != 0);
     return m_condImpl.costPerCoeff(cond_vectorized) +
            (vectorized ? (m_thenImpl.costPerCoeff(vectorized) + m_elseImpl.costPerCoeff(vectorized) +
                           TensorOpCost(0, 0, internal::functor_traits<TernarySelectOp>::Cost, vectorized, PacketSize))
diff --git a/contrib/test/tensor_expr.cpp b/contrib/test/tensor_expr.cpp
index 0eb39e1..9b3fe19 100644
--- a/contrib/test/tensor_expr.cpp
+++ b/contrib/test/tensor_expr.cpp
@@ -278,6 +278,18 @@
   }
 }
 
+// An explicit instantiation instantiates every non-template member, so the condPacket() paths that a bool condition
+// over these branch types cannot take must be member templates.
+template struct Eigen::TensorEvaluator<
+    const Eigen::TensorSelectOp<const Tensor<bool, 1>, const Tensor<float, 1>, const Tensor<float, 1>>,
+    Eigen::DefaultDevice>;
+template struct Eigen::TensorEvaluator<
+    const Eigen::TensorSelectOp<const Tensor<bool, 1>, const Tensor<double, 1>, const Tensor<double, 1>>,
+    Eigen::DefaultDevice>;
+template struct Eigen::TensorEvaluator<
+    const Eigen::TensorSelectOp<const Tensor<bool, 1>, const Tensor<int, 1>, const Tensor<int, 1>>,
+    Eigen::DefaultDevice>;
+
 static void test_select() {
   using TypedGTOp = internal::scalar_cmp_op<float, float, internal::cmp_GT, true>;
 
diff --git a/test/numext.cpp b/test/numext.cpp
index 0373d13..fc88c58 100644
--- a/test/numext.cpp
+++ b/test/numext.cpp
@@ -465,7 +465,9 @@
   volatile T parts[4] = {a.real(), a.imag(), b.real(), b.imag()};
   std::fenv_t environment;
   if (std::feholdexcept(&environment) != 0) return -1;
-  const std::complex<T> q = numext::divide(std::complex<T>(parts[0], parts[1]), std::complex<T>(parts[2], parts[3]));
+  // MSVC's std::complex<T>(const T&, const T&) cannot bind a volatile T, so read each part into a temporary.
+  const std::complex<T> q =
+      numext::divide(std::complex<T>(T(parts[0]), T(parts[1])), std::complex<T>(T(parts[2]), T(parts[3])));
   volatile T result[2] = {q.real(), q.imag()};
   EIGEN_UNUSED_VARIABLE(result);
   const int exceptions = std::fetestexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INVALID | FE_DIVBYZERO);
diff --git a/test/serializer.cpp b/test/serializer.cpp
index 6009017..4361fca 100644
--- a/test/serializer.cpp
+++ b/test/serializer.cpp
@@ -228,7 +228,8 @@
 void test_malformed_sparse_matrix() {
   using T = SparseMatrix<float, Options, int>;
   using Header = typename Serializer<T>::Header;
-  constexpr bool kRowMajor = T::IsRowMajor;
+  // static: MSVC 19.29 wants a captureless lambda to capture even a constexpr local.
+  static constexpr bool kRowMajor = T::IsRowMajor;
   // 3 x 2 with entries (0, 0), (2, 0), (1, 1): compressed, the payload is outer[3], inner[3], values[3] in column-major
   // order, and outer[4], inner[3], values[3] in row-major order.
   T m(3, 2);
diff --git a/test/shape_checks_compatibility.cpp b/test/shape_checks_compatibility.cpp
index 4f3ff4e..b2fefbd 100644
--- a/test/shape_checks_compatibility.cpp
+++ b/test/shape_checks_compatibility.cpp
@@ -48,7 +48,7 @@
 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<0>(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);