Skip to content

<xutility>: Usage of decltype(auto) in the operator() of ranges::iter_move's type seems buggy #5555

Description

Describe the bug

TL; DR: IIUC VSO-1308657 points to a bug of MSVC STL, not of the compiler.

The operator() of ranges::iter_move's type (std::ranges::_Iter_move::_Cpo) currently uses deduced return type:

STL/stl/inc/xutility

Lines 888 to 906 in 7841cf8

template <class _Ty>
requires (_Choice<_Ty>._Strategy != _St::_None)
_NODISCARD _STATIC_CALL_OPERATOR constexpr decltype(auto) operator()(_Ty&& _Val) _CONST_CALL_OPERATOR
noexcept(_Choice<_Ty>._No_throw) {
constexpr _St _Strat = _Choice<_Ty>._Strategy;
if constexpr (_Strat == _St::_Custom) {
return iter_move(static_cast<_Ty&&>(_Val)); // intentional ADL
} else if constexpr (_Strat == _St::_Fallback) {
using _Ref = decltype(*static_cast<_Ty&&>(_Val));
if constexpr (is_lvalue_reference_v<_Ref>) {
return _STD move(*static_cast<_Ty&&>(_Val));
} else {
return *static_cast<_Ty&&>(_Val);
}
} else {
_STL_INTERNAL_STATIC_ASSERT(false); // unexpected strategy
}
}

Such strategy unexpectedly interacts with projected, as:

  • many ranges algorithms check concepts for projected<I, Proj>, and then
  • these concepts eventually (via indirectly-readable-impl) check the result type of ranges::iter_move on projected<I, Proj>, however,
  • when ranges::iter_move uses deduced return type, the body of its operator() needs to be instantiated, and hence projected::operator* also needs to be instantiated.

According to the Standard wording, projected::operator* is not defined, and thus such instantiation renders the program ill-formed, no diagnostic required. The known issue VSO-1308657 "Standard Library Header Units: std::projected::operator*() error LNK2019: unresolved external symbol" seems because of this.

MSVC STL adds the function body to projected::operator* as a workaround, which is arguably conforming under the IFNDR umbrella. But such extraneous instantiations seem undesired.

Command-line test case

Not reproducible because STL has the aforementioned workaround.

However, when I attempt to diagnose misuse of projected::operator* by adding static_assert(false); (as implementation of a potential LWG issue; already mailed to LWG Chair -> is LWG-4270 now), the undesired error can be raised from every use of ranges::equal (and many other ranges algorithms).

Expected behavior

No undesired instantiation happens. Call to projected::operator* can be rejected at compile time.

STL version

Every existing version since #565 (which implemented projected).

Additional context

Other implementations have already switched not to use deduced return type for ranges::iter_move:

Other related PRs:

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementSomething can be improvedfixedSomething works now, yay!rangesC++20/23 ranges

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions