Bug 94973 - compile error when trying to use pointer-to-member function as invokable projection to ranges::find()
Summary: compile error when trying to use pointer-to-member function as invokable proj...
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 9.3.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: rejects-valid
Depends on:
Blocks: pmf, ptmf
  Show dependency treegraph
 
Reported: 2020-05-06 15:03 UTC by Daniel Boles
Modified: 2021-08-12 23:02 UTC (History)
3 users (show)

See Also:
Host:
Target: *-*-mingw*
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-05-06 00:00:00


Attachments
test.ii from --save-temps (244.37 KB, application/zip)
2020-05-06 15:50 UTC, Daniel Boles
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Boles 2020-05-06 15:03:46 UTC
`range-v3` has the concept of "invocable projections", i.e. simple transformations that can be applied as predicates to its algorithms. Pointer-to-member-functions should be included and mean 'get the result of calling that on the current element'. However, it seems that invoking a pointer-to-member-function in g++ (but NOT clang++) causes the build to fail.

Given the code, compiled with `g++ -std=c++17` against `ericniebler/range-v3` release `range-v3-0.10.0`, using `g++.exe (Rev2, Built by MSYS2 project) 9.3.0`:

```cpp
#include <range/v3/algorithm/find.hpp>
#include <vector>

auto
main() -> int
{
	struct S {
		int i{};
		auto get_i() const { return i; }
	};
	
	auto const ss = std::vector<S>(10);
	ranges::find(ss, 1, &S::get_i);
	return 0;
}

```

I get a spew of errors:
```none
test.cpp: In function 'int main()':
test.cpp:14:31: error: no match for call to '(const ranges::find_fn) (const std::vector<main()::S>&, int, int (main()::S::*)() const)'
   14 |  ranges::find(ss, 1, &S::get_i);
      |                               ^
In file included from FOO/include/range-v3/range/v3/range_fwd.hpp:24,
                 from FOO/include/range-v3/range/v3/algorithm/find.hpp:18,
                 from test.cpp:1:
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: candidate: 'template<class I, class S, class V, class P> constexpr concepts::return_t<I, typename std::enable_if<(((input_iterator<I> && sentinel_for<S, I>) && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P>::apply<I>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(I, S, const V&, P) const'
  618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
      |                           ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:47:24: note: in expansion of macro 'RANGES_FUNC'
   47 |         constexpr auto RANGES_FUNC(find)(I first, S last, V const & val, P proj = P{})
      |                        ^~~~~~~~~~~
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note:   template argument deduction/substitution failed:
  618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
      |                           ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:47:24: note: in expansion of macro 'RANGES_FUNC'
   47 |         constexpr auto RANGES_FUNC(find)(I first, S last, V const & val, P proj = P{})
      |                        ^~~~~~~~~~~
In file included from FOO/include/range-v3/range/v3/iterator/access.hpp:22,
                 from FOO/include/range-v3/range/v3/iterator/concepts.hpp:30,
                 from FOO/include/range-v3/range/v3/algorithm/find.hpp:22,
                 from test.cpp:1:
FOO/include/range-v3/std/detail/associated_types.hpp: In substitution of 'template<bool B, class T> using enable_if_t = typename ranges::detail::enable_if::apply<T> [with bool B = ranges::readable<std::vector<main()::S> >; T = std::vector<main()::S>]':
FOO/include/range-v3/range/v3/iterator/concepts.hpp:561:19:   required by substitution of 'template<class I> using apply = ranges::detail::enable_if_t<(bool)(readable<I>), I> [with I = std::vector<main()::S>]'
FOO/include/range-v3/range/v3/algorithm/find.hpp:48:15:   required by substitution of 'template<class I, class S, class V, class P> constexpr concepts::return_t<I, typename std::enable_if<(((input_iterator<I> && sentinel_for<S, I>) && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P>::apply<I>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(I, S, const V&, P) const [with I = std::vector<main()::S>; S = int; V = int (main()::S::*)() const; P = ranges::identity]'
test.cpp:14:31:   required from here
FOO/include/range-v3/std/detail/associated_types.hpp:73:15: error: no class template named 'apply' in 'struct ranges::detail::enable_if<false>'
   73 |         using enable_if_t = typename enable_if<B>::template apply<T>;
      |               ^~~~~~~~~~~
In file included from FOO/include/range-v3/range/v3/range_fwd.hpp:24,
                 from FOO/include/range-v3/range/v3/algorithm/find.hpp:18,
                 from test.cpp:1:
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note: candidate: 'template<class Rng, class V, class P> constexpr concepts::return_t<typename ranges::detail::if_then<forwarding_range_<R> >::apply<decltype (ranges::_::begin(declval<Rng&>())), ranges::dangling>, typename std::enable_if<((input_range<Rng> && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P1>::apply<decltype (ranges::_::begin(declval<Rng&>()))>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(Rng&&, const V&, P) const'
  618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
      |                           ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:60:24: note: in expansion of macro 'RANGES_FUNC'
   60 |         constexpr auto RANGES_FUNC(find)(Rng && rng, V const & val, P proj = P{})
      |                        ^~~~~~~~~~~
FOO/include/range-v3/range/v3/detail/config.hpp:618:27: note:   template argument deduction/substitution failed:
  618 | #define RANGES_FUNC(NAME) operator() RANGES_FUNC_CONST_ /**/
      |                           ^~~~~~~~
FOO/include/range-v3/range/v3/algorithm/find.hpp:60:24: note: in expansion of macro 'RANGES_FUNC'
   60 |         constexpr auto RANGES_FUNC(find)(Rng && rng, V const & val, P proj = P{})
      |                        ^~~~~~~~~~~
In file included from FOO/include/range-v3/range/v3/iterator/access.hpp:22,
                 from FOO/include/range-v3/range/v3/iterator/concepts.hpp:30,
                 from FOO/include/range-v3/range/v3/algorithm/find.hpp:22,
                 from test.cpp:1:
FOO/include/range-v3/std/detail/associated_types.hpp: In substitution of 'template<bool B, class T> using enable_if_t = typename ranges::detail::enable_if::apply<T> [with bool B = ranges::indirectly_regular_unary_invocable<int (main()::S::*)() const, __gnu_cxx::__normal_iterator<const main()::S*, std::vector<main()::S> > >; T = ranges::detail::projected_<__gnu_cxx::__normal_iterator<const main()::S*, std::vector<main()::S> >, int (main()::S::*)() const>]':
FOO/include/range-v3/range/v3/iterator/concepts.hpp:552:19:   required by substitution of 'template<class Proj> template<class I> using apply = ranges::detail::enable_if_t<(bool)(indirectly_regular_unary_invocable<Proj, I>), ranges::detail::projected_<I, Proj> > [with I = __gnu_cxx::__normal_iterator<const main()::S*, std::vector<main()::S> >; Proj = int (main()::S::*)() const]'
FOO/include/range-v3/range/v3/algorithm/find.hpp:61:15:   required by substitution of 'template<class Rng, class V, class P> constexpr concepts::return_t<typename ranges::detail::if_then<forwarding_range_<R> >::apply<decltype (ranges::_::begin(declval<Rng&>())), ranges::dangling>, typename std::enable_if<((input_range<Rng> && indirect_relation<ranges::equal_to, typename ranges::detail::select_projected_<P1>::apply<decltype (ranges::_::begin(declval<Rng&>()))>, const V*>) && concepts::detail::CPP_true(concepts::detail::Nil{})), void>::type> ranges::find_fn::operator()(Rng&&, const V&, P) const [with Rng = const std::vector<main()::S>&; V = int; P = int (main()::S::*)() const]'
test.cpp:14:31:   required from here
FOO/include/range-v3/std/detail/associated_types.hpp:73:15: error: no class template named 'apply' in 'struct ranges::detail::enable_if<false>'
   73 |         using enable_if_t = typename enable_if<B>::template apply<T>;
      |               ^~~~~~~~~~~

shell returned 1
```

* Making the projection a lambda works but seems wasted typing.
* Referring directly to the data member works but is not possible if it should be encapsulated behind a const getter, transformer, etc.
* `clang++` (also on MSYS2) **does** work here. So I guess this must be a bug in `g++`.
Comment 1 Jonathan Wakely 2020-05-06 15:24:51 UTC
Please read https://gcc.gnu.org/bugs and provide the missing information.

Why is this marked as a libstdc++ bug?
Comment 2 Jonathan Wakely 2020-05-06 15:40:08 UTC
I can't reproduce this using range-v3 0.10.0 and GCC 9.3.0 on GNU/Linux, the example compiles fine.
Comment 3 Daniel Boles 2020-05-06 15:48:51 UTC
> Please read https://gcc.gnu.org/bugs and provide the missing information.

Fair point. Let me know if I missed anything still.

    the exact version of GCC;
g++.exe (Rev2, Built by MSYS2 project) 9.3.0

    the system type;
Windows, g++ provided by MSYS2/MinGW64 projects

    the options given when GCC was configured/built;
I didn't build it. Is there a switch that can get this for you?

    the complete command line that triggers the bug;
g++ -I /home/ME/.local/include/range-v3 -std=c++17 -Wall -Wextra -Wpedantic test.cpp

    the compiler output (error messages, warnings, etc.);
already included.

    the preprocessed file (*.i*) that triggers the bug, generated by adding -save-temps to the complete compilation command
I will attach this next.


> Why is this marked as a libstdc++ bug?

I chose libstdc++ because I figured it was more likely that some corner case of std::invoke() was ultimately being hit, rather than the core compiler being at fault. I guess not.


> I can't reproduce this using range-v3 0.10.0 and GCC 9.3.0 on GNU/Linux, the example compiles fine.

Curioser and curioser. It's fine on clang++ on Windows too. Seems a corner case.
Comment 4 Daniel Boles 2020-05-06 15:50:23 UTC
Created attachment 48470 [details]
test.ii from --save-temps
Comment 5 Jonathan Wakely 2020-05-06 16:03:29 UTC
I can reproduce it using x86_64-w64-mingw32-g++ 9.2.1

Reduced:

#include <range/v3/algorithm/find.hpp>

struct X {
  int get_i() const { return 0; }
};

static_assert( ranges::indirectly_regular_unary_invocable<int (X::*)() const, X*> );


Th static assert fails with mingw but not with a native compiler on linux.

I'm not yet convinced this isn't a ranges-v3 bug.
Comment 6 Jonathan Wakely 2020-05-06 16:04:23 UTC
(In reply to DB from comment #3)
> I didn't build it. Is there a switch that can get this for you?

As it says on that page, "the first three of which can be obtained from the output of gcc -v"

But I can reproduce it now anyway.
Comment 7 Jonathan Wakely 2020-05-06 16:16:42 UTC
Further reduced: 

#include <range/v3/algorithm/find.hpp>

struct X { };

using F = int (X::*)() const;
using I = X*;
using R1 = ranges::invoke_result_t<F&, X&>;
static_assert( std::is_same_v<R1, int> );

This fails because R1 is int (X::)() const which is nonsense.


(In reply to DB from comment #3)
> I chose libstdc++ because I figured it was more likely that some corner case
> of std::invoke() was ultimately being hit, rather than the core compiler
> being at fault. I guess not.

std::invoke is not even used, ranges-v3 provides its own invoke.
Comment 8 Daniel Boles 2020-05-06 16:17:54 UTC
> I can reproduce it using x86_64-w64-mingw32-g++ 9.2.1

Thanks again for testing!


> I'm not yet convinced this isn't a ranges-v3 bug.

I will of course defer to your expertise! It could well be caused by something buried somewhere in range-v3's many layers of supporting code. If it can't be a g++ bug, of course just say the word, and I will just forward this all to them.


> As it says on that page, "the first three of which can be obtained from the output of gcc -v"

D'oh. Here we are:

$ g++ -v
Using built-in specs.
COLLECT_GCC=C:\msys64\mingw64\bin\g++.exe
COLLECT_LTO_WRAPPER=C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.3.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../gcc-9.3.0/configure --prefix=/mingw64 --with-local-prefix=/mingw64/local --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --with-native-system-header-dir=/mingw64/x86_64-w64-mingw32/include --libexecdir=/mingw64/lib --enable-bootstrap --with-arch=x86-64 --with-tune=generic --enable-languages=c,lto,c++,fortran,ada,objc,obj-c++ --enable-shared --enable-static --enable-libatomic --enable-threads=posix --enable-graphite --enable-fully-dynamic-string --enable-libstdcxx-filesystem-ts=yes --enable-libstdcxx-time=yes --disable-libstdcxx-pch --disable-libstdcxx-debug --disable-isl-version-check --enable-lto --enable-libgomp --disable-multilib --enable-checking=release --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --enable-plugin --with-libiconv --with-system-zlib --with-gmp=/mingw64 --with-mpfr=/mingw64 --with-mpc=/mingw64 --with-isl=/mingw64 --with-pkgversion='Rev2, Built by MSYS2 project' --with-bugurl=https://sourceforge.net/projects/msys2 --with-gnu-as --with-gnu-ld
Thread model: posix
gcc version 9.3.0 (Rev2, Built by MSYS2 project)
Comment 9 Jonathan Wakely 2020-05-06 16:18:21 UTC
If it used std::invoke it would compile:

static_assert( std::is_same_v<std::invoke_result_t<F&, X&>, int> );    // OK
static_assert( std::is_same_v<ranges::invoke_result_t<F&, X&>, int> ); // ERROR
Comment 10 Jonathan Wakely 2020-05-06 16:26:58 UTC
Aha, the same problem happens on linux if I compile with -fms-extensions

This is the old MS extension that causes x.*f to be accepted when f is a pointer to member function, which should only be valid when used as (x.*f)().

That causes ranges::invoke to think that the projection is a pointer to data member, when actually it's a pointer to member function.

See also PR 94771 comment 4.

Jason, do we want to disable that extension in SFINAE contexts?
Comment 11 Jonathan Wakely 2020-05-06 16:27:14 UTC
Oops, CCing Jason this time.
Comment 12 Jonathan Wakely 2020-05-06 16:30:27 UTC
-fno-ms-extensions will allow you to compile it, as long as you aren't relying on any of the other MSVC compatibility quirks.
Comment 13 Richard Biener 2020-05-06 16:32:43 UTC
Does MSVC still accept that [without diagnostic]?  Maybe it's time to remove it completely...
Comment 14 Jonathan Wakely 2020-05-06 16:39:52 UTC
No (see PR 94771 comment 4)
Comment 15 Daniel Boles 2020-05-06 16:42:31 UTC
Aha, many thanks for the findings.

IMO the MS extensions should really be off by default, esp if compiling in a Standard mode, until the user actually says they want them... right? They seem liable to lead to issues. And might explain lots of other weird warning spam I got earlier... (All I do special is pass -mwindows, just to avoid a spurious terminal)
Comment 16 Daniel Boles 2020-05-06 16:46:05 UTC
> -fno-ms-extensions will allow you to compile it, as long as you aren't relying on any of the other MSVC compatibility quirks.

That indeed fixes the problem, as well as squashing lots of other spurious warnings (some of which default to errors!) from libsigc++ in the same project that were Windows-specific. Thanks!
Comment 17 Jonathan Wakely 2020-05-06 16:47:42 UTC
They're on by default for mingw, for compatibility with the MS compiler (but in this case it seems the relevant extension is ancient history).