Bug 77911 - Comparing function pointers in a constexpr function can produce an error.
Summary: Comparing function pointers in a constexpr function can produce an error.
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 6.2.0
: P3 normal
Target Milestone: 12.0
Assignee: Not yet assigned to anyone
URL:
Keywords: rejects-valid
: 86607 (view as bug list)
Depends on: 69681
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2016-10-09 15:58 UTC by Dr Hilbert Swan
Modified: 2022-01-06 15:50 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-08-05 00:00:00


Attachments
Compiler .ii output to reproduce. (296 bytes, text/plain)
2016-10-09 15:58 UTC, Dr Hilbert Swan
Details
Version that compiles. (291 bytes, text/plain)
2016-10-09 15:59 UTC, Dr Hilbert Swan
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Dr Hilbert Swan 2016-10-09 15:58:06 UTC
Created attachment 39776 [details]
Compiler .ii output to reproduce.

I'm attaching two .ii files one that compiles and one one that fails to compile. There is only one difference between the two files, which is the line:

constexpr unsigned int loc = FindMatchingIdx(&test1, 0);

in the file that compiles, and the file that doesn't compile has:

constexpr unsigned int loc = FindMatchingIdx(&test2, 0);

The difference being &test1 vs &test2.

The code snippet has a constexpr array of function pointers, and then tries to find a function's index in the array at compile time using the constexpr function FindMatchingIdx. For some reason the code compiles when FindMatchingIdx is called with the first element in the array, but fails when called with any other function in the array.

I've tested this code also on clang and MSVC, where it compiles.

I've used gcc.godbolt.org (https://godbolt.org/g/vunD7M) to test various GCC builds, and it appears to compile on 4.9.4 but fails on 5.1 and all later builds.


GCC Version: 6.2.0 20160901 (Ubuntu 6.2.0-3ubuntu11~14.04)
Command line: gcc-6 -v -save-temps -std=c++14 doesnt_compile.cpp


Full gcc output:

swan@pond:/test$ gcc-6 -v -save-temps -std=c++14 doesnt_compile.cpp
Using built-in specs.
COLLECT_GCC=gcc-6
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 6.2.0-3ubuntu11~14.04' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=gcc4-compatible --disable-libstdcxx-dual-abi --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 6.2.0 20160901 (Ubuntu 6.2.0-3ubuntu11~14.04)
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++14' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/6/cc1plus -E -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE doesnt_compile.cpp -mtune=generic -march=x86-64 -std=c++14 -fpch-preprocess -fstack-protector -Wformat -Wformat-security -o doesnt_compile.ii
ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/6"
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/6/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/6
 /usr/include/x86_64-linux-gnu/c++/6
 /usr/include/c++/6/backward
 /usr/lib/gcc/x86_64-linux-gnu/6/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/6/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++14' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/6/cc1plus -fpreprocessed doesnt_compile.ii -quiet -dumpbase doesnt_compile.cpp -mtune=generic -march=x86-64 -auxbase doesnt_compile -std=c++14 -version -fstack-protector -Wformat -Wformat-security -o doesnt_compile.s
GNU C++14 (Ubuntu 6.2.0-3ubuntu11~14.04) version 6.2.0 20160901 (x86_64-linux-gnu)
        compiled by GNU C version 6.2.0 20160901, GMP version 5.1.3, MPFR version 3.1.3, MPC version 1.0.1, isl version 0.15
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
GNU C++14 (Ubuntu 6.2.0-3ubuntu11~14.04) version 6.2.0 20160901 (x86_64-linux-gnu)
        compiled by GNU C version 6.2.0 20160901, GMP version 5.1.3, MPFR version 3.1.3, MPC version 1.0.1, isl version 0.15
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: c78046b3d524e42b67cb421e2693df24
doesnt_compile.cpp:14:45:   in constexpr expansion of ‘FindMatchingIdx(test2, 0)’
doesnt_compile.cpp:11:18: error: ‘(test1 != test2)’ is not a constant expression
     return (work == funcs[idx]) ? (idx) : (FindMatchingIdx(work, idx + 1));
            ~~~~~~^~~~~~~~~~~~~~
Comment 1 Dr Hilbert Swan 2016-10-09 15:59:26 UTC
Created attachment 39777 [details]
Version that compiles.
Comment 2 Dr Hilbert Swan 2016-10-11 16:54:39 UTC
If a similar thing is done with ints the code would look like this:

constexpr int i[] = { 1, 2, 3 };

constexpr int FindMatchingIdx(const int w, const int idx) {
    return (w == i[idx]) ? (idx) : (FindMatchingIdx(w, idx + 1));
}

constexpr unsigned int loc = FindMatchingIdx(2, 0);

int main() {
    return loc;
}

Which compiles correctly. (https://godbolt.org/g/mYpxn9) I've also tried float and doubles, which are fine, but there something about function pointers that gcc doesn't like.

An alternative method using recursive template meta programming fails to compile with a very similar error: (https://godbolt.org/g/zanwsd)

void test1(){}void test2(){}
void test3(){}void test4(){}


typedef void(*Type)();
constexpr Type i[] = { &test1, &test2, &test3 };

template<Type f, int idx, bool equal>
struct FindMatchingIdx2 {
    enum { value = FindMatchingIdx2<f, idx + 1, (f == i[idx + 1])>::value };
};

template<Type f, int idx>
struct FindMatchingIdx2<f, idx, true> {
    enum { value = (idx) };
};

template<Type f>
struct FindMatchingIdx {
  enum { flag = FindMatchingIdx2<f,0, (f == i[0])>::value };
};

int main() {
    return FindMatchingIdx<test1>::flag;
}
Comment 3 Dr Hilbert Swan 2016-10-12 17:40:10 UTC
A generous fellow offered me this workaround for the time being:

   void test1(){}
   void test2(){}
   void test3(){}
   void test4(){}
   #define FUNCTION_LIST test1, test2, test3, test4

   template <int N, void (&Fun)(), void (&Head)(), void (& ...Tail)()>
   struct SearchFun
   {
       static constexpr int Idx = SearchFun<N + 1, Fun, Tail...>::Idx;
   };

   template <int N, void (&Fun)(), void (& ...Tail)()>
   struct SearchFun<N, Fun, Fun, Tail...>
   {
       static constexpr int Idx = N;
   };

   template <void (&Fun)()>
   using FindMatchingIdx = SearchFun<0, Fun, FUNCTION_LIST>;

   // Tests
   constexpr unsigned int loc1 = FindMatchingIdx<test1>::Idx;
   static_assert(loc1 == 0, "");
   constexpr unsigned int loc2 = FindMatchingIdx<test2>::Idx;
   static_assert(loc2 == 1, "");
   constexpr unsigned int loc3 = FindMatchingIdx<test3>::Idx;
   static_assert(loc3 == 2, "");

   // If the array is needed
   typedef void (*func)();
   func funcs[] = { FUNCTION_LIST };

   // If desired the template alias can be wrapped in a macro

   #define FindMatching(Fun) FindMatchingIdx<Fun>::Idx
   constexpr unsigned int loc4 = FindMatching(test2);
   static_assert(loc4 == 1, "");
Comment 4 Andrew Pinski 2021-08-05 02:31:26 UTC
Confirmed,  I suspect this is the same problem as PR 69681.
Comment 5 Andrew Pinski 2021-08-05 02:32:56 UTC
*** Bug 86607 has been marked as a duplicate of this bug. ***
Comment 6 Patrick Palka 2022-01-06 15:50:34 UTC
This one is also fixed for GCC 12 by r12-6188.