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)); ~~~~~~^~~~~~~~~~~~~~
Created attachment 39777 [details] Version that compiles.
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; }
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, "");
Confirmed, I suspect this is the same problem as PR 69681.
*** Bug 86607 has been marked as a duplicate of this bug. ***
This one is also fixed for GCC 12 by r12-6188.