Skip to content

XTENSOR_USE_XSIMD: hard compile error for reshape_view over a lazy expression (lvalue required as unary '&' in xstrided_view::load_simd) #2929

Description

@Roy-Kid

Summary

With XTENSOR_USE_XSIMD defined, assigning from an expression that contains
xt::reshape_view(<lazy expression>, shape) fails to compile with

xtensor/views/xstrided_view.hpp:688:60: error: lvalue required as unary ‘&’ operand
  688 |         return xt_simd::load_as<requested_type>(&(storage()[i]), align_mode());
      |                                                  ~~~~~~~~~~^~~~

The same code compiles and produces correct results without XTENSOR_USE_XSIMD,
and it also compiles with xsimd if the argument of reshape_view is an
evaluated container instead of a lazy expression. So the trigger is specifically
reshape_view over an unevaluated xfunction while the SIMD assign path is enabled.

This is a hard error inside a template body, not an SFINAE-friendly rejection, so
it cannot be worked around by the caller other than by pre-evaluating the operand
or by not enabling xsimd globally.

Reproducer (fails)

#include <cstddef>
#include <xtensor/containers/xtensor.hpp>
#include <xtensor/views/xstrided_view.hpp>

int main() {
    const std::size_t G = 8, N = 4;
    xt::xtensor<double, 1> w({G}, 1.5), v({G}, 2.0);
    xt::xtensor<double, 2> Phi({G, N}, 3.0);

    auto col = xt::reshape_view(w * v, {G, std::size_t(1)});  // lazy xfunction
    xt::xtensor<double, 2> out = Phi * col;                   // broadcast assign
    return out(0, 0) == 9.0 ? 0 : 1;
}
g++ -std=c++20 -O2 -march=native -DXTENSOR_USE_XSIMD repro.cpp   # COMPILE ERROR
g++ -std=c++20 -O2 -march=native                     repro.cpp   # OK, correct

Control (compiles, with and without xsimd)

Pre-evaluating the operand makes the strided view container-backed and everything works:

xt::xtensor<double, 1> wv = w * v;                       // evaluated
auto col = xt::reshape_view(wv, {G, std::size_t(1)});
xt::xtensor<double, 2> out = Phi * col;                  // OK with -DXTENSOR_USE_XSIMD

Measured matrix:

operand of reshape_view XTENSOR_USE_XSIMD off on
lazy w * v compiles, correct compile error
evaluated xtensor<double,1> compiles, correct compiles, correct

Root cause (as far as I can trace it)

xstrided_view gates its SIMD interface on the wrapped expression:

// xtensor/views/xstrided_view.hpp:183
static constexpr bool provides_simd_interface = has_simd_interface<xexpression_type>::value
                                               && L != layout_type::dynamic;

For reshape_view(w * v, …) the xexpression_type is xfunction<multiplies, …>,
which does provide a SIMD interface, so provides_simd_interface is true and
xassign selects linear_assigner<simd_assign=true>.

But load_simd / store_simd do not delegate to xexpression_type::load_simd
they take the address of the view's own flat storage:

// xtensor/views/xstrided_view.hpp:688 (load_simd), :678 (store_simd)
return xt_simd::load_as<requested_type>(&(storage()[i]), align_mode());

For a lazy operand the FST is detail::flat_adaptor_getter<xclosure_t<E>, L>
(xstrided_view.hpp:870), whose storage type is
detail::flat_expression_adaptor<xfunction<…>> (xstrided_view_base.hpp:251-263).
Its reference / const_reference are inherited from the wrapped expression
(xstrided_view_base.hpp:40-44), and for an xfunction those are prvalues
(computed on access), not lvalue references. Taking & of that is ill-formed.

So provides_simd_interface tests the wrong property: it asks "can the wrapped
expression vectorize?", while the two functions it guards actually require "does
this view's storage expose a contiguous lvalue buffer?".

Suggested direction

Add the storage requirement to the constraint, e.g. also require that
decltype(std::declval<const storage_type&>()[0]) is an lvalue reference (or,
equivalently, that FST is not a flat_adaptor_getter over a non-container
expression), so that lazy-expression-backed strided views simply fall back to the
non-SIMD assign path instead of hard-erroring. Alternatively, load_simd/store_simd
could forward to the underlying expression's own load_simd/store_simd when the
flat storage is a computed adaptor.

Environment

  • xtensor 0.27.1 (also xtl 0.8.2, xsimd 14.2.0), fetched via CMake FetchContent
  • GCC 14.3.0, -std=c++20 -O2 -march=native (AVX-512 target: avx512vnni/avx512vbmi2)
  • Linux x86_64

I searched open/closed issues for load_simd, xstrided_view simd,
reshape_view XTENSOR_USE_XSIMD and lvalue required and did not find this one;
#2025 (where + xsimd) is a runtime-value bug on a different path, so I opened a
separate report. Happy to test a patch — the reproducer above is the whole case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions