Skip to content

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

@frederick-vs-ja

Description

@frederick-vs-ja

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:

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementSomething can be improvedrangesC++20/23 ranges

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions