https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101460

            Bug ID: 101460
           Summary: Useless cascade of overload resolution errors for
                    invalid expression
           Product: gcc
           Version: 12.0
            Status: UNCONFIRMED
          Keywords: diagnostic
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: redi at gcc dot gnu.org
  Target Milestone: ---

template<bool> struct enable_if { };
template<> struct enable_if<true> { using type = void; };

template<bool B>
using enable_if_t = typename enable_if<B>::type;

struct tuple { };
struct pair { };

template<unsigned N> enable_if_t<N == 1> get(tuple&) { }
template<unsigned N> enable_if_t<N == 1> get(const tuple&) { }
template<unsigned N> enable_if_t<N == 1> get(pair&) { }
template<unsigned N> enable_if_t<N == 1> get(const pair&) { }

template<int N>
constexpr unsigned
frob()
{
  static_assert(N == 1, "user-friendly diagnostic");
  return unsigned{N}; // narrowing check, reject negative values
}

template<int N> void get_n(tuple& t) { get<frob<N>()>(t); }

int main()
{
  tuple t;
  get_n<-1>(t);
}

This prints a wall of errors:

stupid.C: In instantiation of 'constexpr unsigned int frob() [with int N =
-1]':
stupid.C:23:51:   required from 'void get_n(tuple&) [with int N = -1]'
stupid.C:28:12:   required from here
stupid.C:19:19: error: static assertion failed: user-friendly diagnostic
   19 |   static_assert(N == 1, "user-friendly diagnostic");
      |                 ~~^~~~
stupid.C:19:19: note: '(-1 == 1)' evaluates to false
stupid.C:20:20: error: narrowing conversion of '-1' from 'int' to 'unsigned
int' [-Wnarrowing]
   20 |   return unsigned{N}; // narrowing check, reject negative values
      |                    ^
stupid.C:21:1: error: body of 'constexpr' function 'constexpr unsigned int
frob() [with int N = -1]' not a return-statement
   21 | }
      | ^
stupid.C: In instantiation of 'void get_n(tuple&) [with int N = -1]':
stupid.C:28:12:   required from here
stupid.C:23:54: error: no matching function for call to
'get<frob<-1>()>(tuple&)'
   23 | template<int N> void get_n(tuple& t) { get<frob<N>()>(t); }
      |                                        ~~~~~~~~~~~~~~^~~
stupid.C:10:42: note: candidate: 'template<unsigned int N> enable_if_t<(N ==
1)> get(tuple&)'
   10 | template<unsigned N> enable_if_t<N == 1> get(tuple&) { }
      |                                          ^~~
stupid.C:10:42: note:   template argument deduction/substitution failed:
stupid.C:23:51: error: 'constexpr unsigned int frob() [with int N = -1]' called
in a constant expression
   23 | template<int N> void get_n(tuple& t) { get<frob<N>()>(t); }
      |                                            ~~~~~~~^~
stupid.C:17:1: note: 'constexpr unsigned int frob() [with int N = -1]' is not
usable as a 'constexpr' function because:
   17 | frob()
      | ^~~~
stupid.C:23:51: note: in template argument for type 'unsigned int'
   23 | template<int N> void get_n(tuple& t) { get<frob<N>()>(t); }
      |                                            ~~~~~~~^~
stupid.C:11:42: note: candidate: 'template<unsigned int N> enable_if_t<(N ==
1)> get(const tuple&)'
   11 | template<unsigned N> enable_if_t<N == 1> get(const tuple&) { }
      |                                          ^~~
stupid.C:11:42: note:   template argument deduction/substitution failed:
stupid.C:23:51: error: 'constexpr unsigned int frob() [with int N = -1]' called
in a constant expression
   23 | template<int N> void get_n(tuple& t) { get<frob<N>()>(t); }
      |                                            ~~~~~~~^~
stupid.C:23:51: note: in template argument for type 'unsigned int'
stupid.C:12:42: note: candidate: 'template<unsigned int N> enable_if_t<(N ==
1)> get(pair&)'
   12 | template<unsigned N> enable_if_t<N == 1> get(pair&) { }
      |                                          ^~~
stupid.C:12:42: note:   template argument deduction/substitution failed:
stupid.C:23:51: error: 'constexpr unsigned int frob() [with int N = -1]' called
in a constant expression
   23 | template<int N> void get_n(tuple& t) { get<frob<N>()>(t); }
      |                                            ~~~~~~~^~
stupid.C:23:51: note: in template argument for type 'unsigned int'
stupid.C:13:42: note: candidate: 'template<unsigned int N> enable_if_t<(N ==
1)> get(const pair&)'
   13 | template<unsigned N> enable_if_t<N == 1> get(const pair&) { }
      |                                          ^~~
stupid.C:13:42: note:   substitution of deduced template arguments resulted in
errors seen above


This is reduced from libstdc++ where there are more overloads of get<N> and all
of them get tried, and all of them print exactly the same error.

Why are we even attempting overload resolution when the expression is invalid?
Is the invalid non-constant frob<-1>() call going to suddenly become valid if
we keep trying hard enough?

If PR 96286 gets fixed then the static_assert in frob should stop compilation,
but if we remove that from the code above then we'll still get a wall of
unhelpful errors. Constant evaluation of frob<-1>() failed, so stop trying to
use it as a template argument.

Reply via email to