Core: Fix the dot() rewrap of adjoint() operands with non-unit stride

libeigen/eigen!3054

Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
diff --git a/Eigen/src/Core/InnerProduct.h b/Eigen/src/Core/InnerProduct.h
index 6e70f7a..fab797c 100644
--- a/Eigen/src/Core/InnerProduct.h
+++ b/Eigen/src/Core/InnerProduct.h
@@ -289,7 +289,8 @@
 
 template <typename Op, typename Xpr, typename Target>
 struct rewrap_unary<CwiseUnaryOp<Op, Xpr>, Target> {
-  using type = CwiseUnaryOp<Op, typename rewrap_unary<Xpr, Target>::type>;
+  // unaryExpr() nests its operand as const; Xpr itself need not be (adjoint() nests a non-const Transpose).
+  using type = CwiseUnaryOp<Op, std::add_const_t<typename rewrap_unary<Xpr, Target>::type>>;
 
   static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE constexpr type apply(CwiseUnaryOp<Op, Xpr> const& base,
                                                                     Target const& target) {
diff --git a/test/adjoint.cpp b/test/adjoint.cpp
index f07f2c7..417414f 100644
--- a/test/adjoint.cpp
+++ b/test/adjoint.cpp
@@ -239,6 +239,33 @@
   }
 }
 
+// dot() maps operands with a runtime unit stride to contiguous vectors and rewraps their unary ops around the map.
+// adjoint() nests a non-const Transpose, so the rewrap must produce the const-nested type unaryExpr() returns.
+template <typename Scalar>
+void inner_product_adjoint_rewrap() {
+  // Column-major so that row(r) has a compile-time non-unit stride and the raw maps below walk a row and a column.
+  using Mat = Matrix<Scalar, Dynamic, Dynamic, ColMajor>;
+  using Vec = Matrix<Scalar, Dynamic, 1>;
+  using StridedMap = Map<const Vec, Unaligned, InnerStride<Dynamic>>;
+  const Index n = internal::random<Index>(1, EIGEN_TEST_MAX_SIZE);
+  const Index r = internal::random<Index>(0, n - 1);
+  Mat m = Mat::Random(n, n);
+  Vec v = Vec::Random(n);
+  Scalar expected(0);
+  for (Index k = 0; k < n; ++k) expected += m(r, k) * v(k);
+
+  // Compile-time non-unit stride: the map path is instantiated but not taken.
+  VERIFY_IS_APPROX(m.row(r).adjoint().dot(v), expected);
+  VERIFY_IS_APPROX(v.dot(m.row(r).adjoint()), numext::conj(expected));
+  // Runtime strides: n skips the map, 1 takes it.
+  StridedMap row(m.data() + r, n, InnerStride<Dynamic>(n));
+  StridedMap column(m.data() + r * n, n, InnerStride<Dynamic>(1));
+  VERIFY_IS_APPROX(row.adjoint().dot(v), expected);
+  VERIFY_IS_APPROX(v.dot(row.adjoint()), numext::conj(expected));
+  VERIFY_IS_APPROX(column.adjoint().dot(v), (m.col(r).transpose() * v).value());
+  VERIFY_IS_APPROX(v.dot(column.adjoint()), numext::conj((m.col(r).transpose() * v).value()));
+}
+
 // Test transposeInPlace at vectorization boundary sizes.
 // BlockedInPlaceTranspose uses PacketSize-blocked loops with a scalar remainder (line 273),
 // exercising off-by-one-prone transitions.
@@ -310,6 +337,11 @@
   CALL_SUBTEST_15(inner_product_boundary_sizes<double>());
   CALL_SUBTEST_16(inner_product_boundary_sizes<std::complex<float>>());
   CALL_SUBTEST_17(inner_product_boundary_sizes<std::complex<double>>());
+  for (int i = 0; i < g_repeat; i++) {
+    CALL_SUBTEST_19(inner_product_adjoint_rewrap<double>());
+    CALL_SUBTEST_19(inner_product_adjoint_rewrap<std::complex<float>>());
+    CALL_SUBTEST_19(inner_product_adjoint_rewrap<std::complex<double>>());
+  }
 
   // transposeInPlace at vectorization boundaries (deterministic, outside g_repeat).
   CALL_SUBTEST_18(transposeInPlace_boundary<float>());