Bug 60852 - [4.8/4.9/4.10 Regression] boost::has_complement of enum class does not compile
Summary: [4.8/4.9/4.10 Regression] boost::has_complement of enum class does not compile
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.9.0
: P3 normal
Target Milestone: 4.8.3
Assignee: Paolo Carlini
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-04-15 19:20 UTC by Nevin Liber
Modified: 2014-04-17 06:19 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work: 4.7.2
Known to fail: 4.8.2, 4.9.0
Last reconfirmed: 2014-04-15 00:00:00


Attachments
Reproducible test program (123 bytes, text/x-csrc)
2014-04-15 19:20 UTC, Nevin Liber
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Nevin Liber 2014-04-15 19:20:17 UTC
Created attachment 32608 [details]
Reproducible test program

This works under gcc 4.7.2 and clang 3.4, but fails to compile under gcc 4.9 RC1.  enum class seems to be the only issue; plain enums work fine.

#include <boost/type_traits.hpp>
#include <iostream>

enum class E {};

int main()
{ std::cout << boost::has_complement<E>() << std::endl; }

Using Boost 1.55 and g++ -std=c++11, we get:

In file included from /opt/local/include/boost/config.hpp:57:0,
                 from /opt/local/include/boost/type_traits/add_const.hpp:13,
                 from /opt/local/include/boost/type_traits.hpp:13,
                 from a.cpp:1:
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp: In instantiation of 'const bool boost::detail::has_complement_impl::operator_exists<E>::value':
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp:173:4:   required from 'const bool boost::detail::has_complement_impl::trait_impl1<E, boost::detail::has_complement_impl::dont_care, false>::value'
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp:195:4:   required from 'const bool boost::detail::has_complement_impl::trait_impl<E, boost::detail::has_complement_impl::dont_care>::value'
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp:202:1:   required from 'struct boost::has_complement<E>'
a.cpp:7:41:   required from here
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp:152:56: error: no match for 'operator~' (operand type is 'E')
    BOOST_STATIC_CONSTANT(bool, value = (sizeof(check(((BOOST_TT_TRAIT_OP make<Rhs>()),make<has_operator>())))==sizeof(::boost::type_traits::yes_type)));
                                                        ^
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp: In instantiation of 'const bool boost::detail::has_complement_impl::operator_returns_void<E>::value':
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp:173:4:   required from 'const bool boost::detail::has_complement_impl::trait_impl1<E, boost::detail::has_complement_impl::dont_care, false>::value'
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp:195:4:   required from 'const bool boost::detail::has_complement_impl::trait_impl<E, boost::detail::has_complement_impl::dont_care>::value'
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp:202:1:   required from 'struct boost::has_complement<E>'
a.cpp:7:41:   required from here
/opt/local/include/boost/type_traits/detail/has_prefix_operator.hpp:89:102: error: no match for 'operator~' (operand type is 'E')
    BOOST_STATIC_CONSTANT(bool, value = (sizeof(::boost::type_traits::yes_type)==sizeof(returns_void((BOOST_TT_TRAIT_OP make<Rhs>(),returns_void_t())))));
                                                                                                      ^
a.cpp: In function 'int main()':
a.cpp:7:41: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
 { std::cout << boost::has_complement<E>() << std::endl; }
                                         ^
In file included from /opt/local/include/gcc49/c++/istream:39:0,
                 from /opt/local/include/gcc49/c++/sstream:38,
                 from /opt/local/include/gcc49/c++/complex:45,
                 from /opt/local/include/boost/type_traits/is_complex.hpp:12,
                 from /opt/local/include/boost/type_traits.hpp:49,
                 from a.cpp:1:
/opt/local/include/gcc49/c++/ostream:602:5: note: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = boost::has_complement<E>]'
     operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
     ^
Comment 1 Nevin Liber 2014-04-15 19:37:55 UTC
Also filed this as a Boost bug at https://svn.boost.org/trac/boost/ticket/9913
Comment 2 Jonathan Wakely 2014-04-15 20:02:13 UTC
Preprocessed source, please. And ideally reduced so it doesn't contain a billion lines of MPL!
Comment 3 Markus Trippelsdorf 2014-04-15 20:24:08 UTC
reduced test case:

struct A;
typedef A false_;
struct A
{
};
template <int> struct B;
template <> struct B<false> : false_
{
};
template <int> struct C
{
  static const int value = 0;
};
template <typename T> T &make ();
struct D
{
  template <class T> D (T);
};
template <typename, typename, int> struct G;
template <typename Rhs> struct H
{
  static const int value = G<Rhs, int, 0>::value;
};
template <typename Rhs> struct I : B<H<Rhs>::value>
{
};
enum class E;
int operator~(const D &);
template <typename Rhs> struct F
{
  static const int value = sizeof ~make<Rhs>();
};
template <typename Rhs, typename Ret> struct G<Rhs, Ret, 0>
{
  static const int value = C<F<Rhs>::value>::value;
};
int main () { I<E>(); }

markus@x4 tmp % g++ -std=c++11 boost.ii
boost.ii: In instantiation of ‘const int F<E>::value’:
boost.ii:35:20:   required from ‘const int G<E, int, 0>::value’
boost.ii:22:20:   required from ‘const int H<E>::value’
boost.ii:24:32:   required from ‘struct I<E>’
boost.ii:37:20:   required from here
boost.ii:31:35: error: no match for ‘operator~’ (operand type is ‘E’)
   static const int value = sizeof ~make<Rhs>();
                                   ^
Comment 4 Richard Biener 2014-04-16 08:33:58 UTC
Confirmed.  Not sure if the code is valid.
Comment 5 Jonathan Wakely 2014-04-16 09:31:45 UTC
Further reduced:

struct D
{
  template <class T> D (T);
};
int operator~(const D &);

template <typename T> T &make ();

template <typename Rhs> struct H
{
  static const int value = sizeof ~make<Rhs>();
};

enum class E;

int main () { return H<E>::value; }
Comment 6 Jakub Jelinek 2014-04-16 09:32:42 UTC
Started to be rejected with r192471 aka PR17805.
Comment 7 Jonathan Wakely 2014-04-16 09:35:35 UTC
Both clang and G++ reject:

  static const int value = sizeof ~make<E>();

Whereas clang accepts (and G++ rejects) the same expression using a dependent type:

template <typename Rhs> struct H
{
  static const int value = sizeof ~make<Rhs>();
};

Clang constructs a temporary D and applies operator~ to that temporary
Comment 8 Paolo Carlini 2014-04-16 10:05:20 UTC
I'm about to take a few days off, but note that the patch from Alexandre which I moved forward fixed an accept invalid. Thus if you like, before leaving I can revert it in the branches and return to it for 4.10. Let me know.
Comment 9 Jakub Jelinek 2014-04-16 11:23:00 UTC
Given that the patch has been there for quite some time already, I think it is important to first understand if this testcase is valid or not, before considering reversion.
CCing Alex as well.
Comment 10 Paolo Carlini 2014-04-16 11:30:04 UTC
Humm, I notice only now that the issue only affects enum classes not plain enums. Thus must be easy to fix.
Comment 11 Jonathan Wakely 2014-04-16 12:03:18 UTC
I don't know if it's valid, but the discrepancy noted in comment 7 is probably relevant, one way or another
Comment 12 Markus Trippelsdorf 2014-04-16 12:36:45 UTC
The guys on stackoverflow think it's invalid:
http://stackoverflow.com/questions/23108590/is-this-valid-c11
Comment 13 Nicolas Silvagni 2014-04-16 13:16:06 UTC
Clang is wrong, only operator taking E or reference to E can contribute.

In 13.3.1.2 §3

For a unary operator @ with an operand of a type whose cv-unqualified version is T1, and for a binary operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type whose cv-unqualified version is T2, three sets of candidate functions, designated member candidates, nonmember candidates and built-in candidates, are constructed as follows:


Follow by :

However, if no operand has a class type, only those non-member functions in the lookup set that have a first parameter of type T1 or “reference to (possibly cv-qualified) T1”, when T1 is an enumeration type, or (if there is a right operand) a second parameter of type T2 or “reference to (possibly cv-qualified) T2”, when T2 is an enumeration type, are candidate functions.
Comment 14 Markus Trippelsdorf 2014-04-16 14:18:07 UTC
Thanks Nicolas. Closing.
Comment 15 Marc Glisse 2014-04-16 20:05:52 UTC
Did someone file a corresponding PR for clang? It is useful for them, but also for gcc because it gives them a chance to give a different interpretation of the standard.
Comment 16 Markus Trippelsdorf 2014-04-16 20:16:14 UTC
(In reply to Marc Glisse from comment #15)
> Did someone file a corresponding PR for clang? It is useful for them, but
> also for gcc because it gives them a chance to give a different
> interpretation of the standard.

Unfortunately filing bugs for clang is like posting to /dev/null 
most of the time.
Comment 17 Jonathan Wakely 2014-04-16 21:04:41 UTC
(In reply to Markus Trippelsdorf from comment #16)
> Unfortunately filing bugs for clang is like posting to /dev/null 
> most of the time.

That's not my experience - if it hasn't been filed yet I'll do so.
Comment 18 Jonathan Wakely 2014-04-16 21:19:27 UTC
http://llvm.org/bugs/show_bug.cgi?id=19452
Comment 19 Markus Trippelsdorf 2014-04-17 06:19:48 UTC
(In reply to Jonathan Wakely from comment #17)
> (In reply to Markus Trippelsdorf from comment #16)
> > Unfortunately filing bugs for clang is like posting to /dev/null 
> > most of the time.
> 
> That's not my experience - if it hasn't been filed yet I'll do so.
> http://llvm.org/bugs/show_bug.cgi?id=19452

I was wrong this time:

 % clang++ -std=c++11 b.ii
b.ii:11:35: error: invalid argument type 'E' to unary expression
  static const int value = sizeof ~make<Rhs>();
                                  ^~~~~~~~~~~~
b.ii:16:22: note: in instantiation of template class 'H<E>' requested here
int main () { return H<E>::value; }
                     ^
1 error generated.