Bug 94890 - std::tuple<aggregate>({0}) fails to compile with -std=c++2a
Summary: std::tuple<aggregate>({0}) fails to compile with -std=c++2a
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: ---
Assignee: Ville Voutilainen
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-04-30 14:32 UTC by Patrick Palka
Modified: 2020-09-27 19:24 UTC (History)
4 users (show)

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


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Patrick Palka 2020-04-30 14:32:44 UTC
$ cat testcase.C
#include <tuple>

struct c { int i; };

std::tuple<c> x({0});

$ g++-10 -std=c++17 testcase.C
$ g++-10 -std=c++2a testcase.C
testcase.C:5:20: error: converting to ‘std::tuple<c>’ from initializer list would use explicit constructor ‘constexpr std::tuple<_Elements>::tuple(_UElements&& ...) [with _UElements = {int}; bool _Valid = true; typename std::enable_if<std::tuple<_Elements>::_TCC<_Valid>::__is_explicitly_constructible<_UElements ...>(), bool>::type <anonymous> = false; _Elements = {c}]’
    5 | std::tuple<c> x({0});
      |                    ^


PR94885 may be related, but its testcase wasn't reduced from this testcase.
Comment 1 Patrick Palka 2020-04-30 22:19:01 UTC
Bisection seems to be pointing to r10-6519, so changing component from libstdc++ to c++.

A reduced testcase:

template <int a> struct b { static const int c = a; };
template <int> struct d;
template <> struct d<0> { typedef int e; };
struct f {};
template <class g, class = decltype(g(0))> f aa(int);
template <class> b<0> aa(...);
struct h {
  int i;
};
template <class...> struct j : decltype(aa<h>(0)) {};
template <bool> using ap = b<1>;
template <template <class...> class aq> ap<typename d<aq<>::c>::e{}> at(int);
template <template <class> class> b<0> at(...);
struct k {
  template <class> static const int ax = decltype(at<j>(0))::c;
  k(...);
  template <class l, typename d<ax<l>>::e = 0> explicit k(l);
};

k a({0});
Comment 2 Patrick Palka 2020-04-30 23:01:47 UTC
Minimal testcase:


template <class e, class = decltype(e(0))>
void foo();

struct t { int a; };

void bar()
{
  foo<t>();
}
Comment 3 Patrick Palka 2020-04-30 23:13:54 UTC
Whoops, the above minimal testcase doesn't actually illustrate any bug, we just correctly accept it in c++2a mode ever since r10-6519.  Hmm...
Comment 4 Marek Polacek 2020-05-01 02:59:34 UTC
My current theory is that it is not a bug.

While processing "decltype(aa<h>(0))" we look for a suitable aa candidate, and thanks to C++20 paren-init "g(0)" in "decltype(g(0))" succeeds.  Then we select  aa(int) rather than aa(...) and due to various SFINAEs we end up choosing the explicit k(l) constructor.  In C++17 the paren-init doesn't kick in and we select "k(...)".  So in C++20 when actually converting {0} to S we hit:

 7426         /* When converting from an init list we consider explicit
 7427            constructors, but actually trying to call one is an error.  */

(DR 1518 + DR 1630)

This fails in C++17 too when

template <class g, class = decltype(g(0))> f aa(int);

is changed to

template <class g, class = decltype(g{0})> f aa(int);
Comment 5 Ville Voutilainen 2020-05-01 14:28:45 UTC
What happens with std::tuple<c> x({0}) is that the _UElements&&... constructor is not a direct candidate, so we fall back to trying a copy constructor. That's a match, and then it tries to convert the argument. Since c is constructible from int, but not convertible from int, the match is the explicit constructor, and calling that fails because initializing the argument is copy-init.

The fix that we can apply in tuple is to make __is_implicitly_constructible
look at either is_convertible or is_aggregate, and then negate properly
in __is_explicitly_constructible.

In other words, mine. :)
Comment 6 Marek Polacek 2020-05-01 14:33:22 UTC
Thanks Ville.  What I should have said...

(In reply to Marek Polacek from comment #4)
> My current theory is that it is not a bug.

...in the compiler proper.  It'd be nice if the original test compiled.
Comment 7 Ville Voutilainen 2020-05-01 14:46:32 UTC
..and as expected, std::optional is broken the same way.
Comment 8 Ville Voutilainen 2020-05-01 14:47:14 UTC
(In reply to Marek Polacek from comment #6)
> Thanks Ville.  What I should have said...
> 
> (In reply to Marek Polacek from comment #4)
> > My current theory is that it is not a bug.
> 
> ...in the compiler proper.  It'd be nice if the original test compiled.

No worries. This needs two library fixes and an LWG issue, I'll take care of it.
Comment 9 Ville Voutilainen 2020-09-27 19:24:01 UTC
LEWG says NO: https://cplusplus.github.io/LWG/issue3440