Regression in Boost.Variant with gcc6.3 in C++03 mode

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Regression in Boost.Variant with gcc6.3 in C++03 mode

Boost - Users mailing list
Hi,

This is the simplest possible repro I could get:

#include <boost/variant.hpp>
class A {};
class B : private A {};
typedef boost::variant<A, B> variant_t;
int main()
{
    B b;
    variant_t v (b);
}

This used to work with Boost 1.59.0 but fails with 1.64.0

 Using wandbox I've found that:
- it started failing in 1.62.0
- clang does not have this problem
- newer and older versions of gcc do have the same problem

The error is miles long. I think the important parts are:

boost/type_traits/is_convertible.hpp:149:53: error: 'A' is an inaccessible base of 'B'
error: no matching function for call to 'boost::variant<A, B>::variant(B&)'

It seems the metaprogramming messes up due to the private inheritance, and decides that the variant<A,B> is *not* constructible from B

Workarounds are very welcome!
Switching to C++14 is not an option (yet).

Regards

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Regression in Boost.Variant with gcc6.3 in C++03 mode

Boost - Users mailing list

> On 27 Jun 2017, at 20:09, dariomt--- via Boost-users <[hidden email]> wrote:
>
> #include <boost/variant.hpp>
> class A {};
> class B : private A {};
> typedef boost::variant<A, B> variant_t;
> int main()
> {
>     B b;
>     variant_t v (b);
> }
>

In `boost/type_traits/is_convertible.hpp`:

```
   123 struct any_conversion
   124 {
   125    template <typename T> any_conversion(const volatile T&);
   126    template <typename T> any_conversion(const T&);
   127    template <typename T> any_conversion(volatile T&);
   128    template <typename T> any_conversion(T&);
   129 };
   130
   131 template <typename T> struct checker
   132 {
   133    static boost::type_traits::no_type _m_check(any_conversion ...);
   134    static boost::type_traits::yes_type _m_check(T, int);
   135 };
```

And according to [https://stackoverflow.com/questions/30004771/why-is-a-malformed-function-used-instead-of-an-implicit-conversion], the standard conversion sequence takes precedence of the user-defined conversion (`any_conversion`), hence `checker<A>::_m_check(B(), 0)` results in a hard error.

So I suspect this is a bug in 'type_traits' library, but I'm not sure. Hopefully somebody else can confirm. (I'm sorry but I don't know to whom should I CC this post.)

An easy workaround would be specializing `boost::is_convertible` for your classes:

```
namespace boost {
template <> struct is_convertible<B&, A> : boost::false_type {};
template <> struct is_convertible<const B&, A> : boost::false_type {};
}

```

I don't know why clang accepts your code though. Perhaps `boost::is_convertible` is conditionally compiled to some different implementations in Clang.

------

A reduced repo of the hard error in `boost::is_convertible` is:

```
struct any_conversion
{
        template <typename T> any_conversion(const volatile T&);
        template <typename T> any_conversion(const T&);
        template <typename T> any_conversion(volatile T&);
        template <typename T> any_conversion(T&);
};

typedef char (&yes_type)[2];
typedef char (&no_type)[1];

class A {};
class B : private A {};

static no_type _m_check(any_conversion ...);
static yes_type _m_check(A, int);

int main() {
        B b;

        _m_check(b, 0);
}
```

Both GCC and Clang reject it.


_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Regression in Boost.Variant with gcc6.3 in C++03 mode

Boost - Users mailing list

2017-06-27 17:46 GMT+02:00 [hidden email] <[hidden email]>:
So I suspect this is a bug in 'type_traits' library, but I'm not sure. Hopefully somebody else can confirm. (I'm sorry but I don't know to whom should I CC this post.)

I don't know why clang accepts your code though. Perhaps `boost::is_convertible` is conditionally compiled to some different implementations in Clang.

Thanks for the analysis.
My guess is that clang uses a different implementation of is_convertible.
Also, I guess this works in C++14 because std::is_convertible is used instead?

Regards 

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Regression in Boost.Variant with gcc6.3 in C++03 mode

Boost - Users mailing list
In reply to this post by Boost - Users mailing list

> So I suspect this is a bug in 'type_traits' library, but I'm not sure. Hopefully somebody else can confirm. (I'm sorry but I don't know to whom should I CC this post.)

The short answer, is that a fully conforming is_convertible requires
C++11 language features, so your test code is fine in C++11 or 14 mode
but in 03 mode uses legacy code which is known not to work in every
circumstance.

clang works because the clang front end provides an intrinsic for
is_convertible which gcc does not (last I checked).

Of course that doesn't help you much, sorry, John.

---
This email has been checked for viruses by AVG.
http://www.avg.com

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Regression in Boost.Variant with gcc6.3 in C++03 mode

Boost - Users mailing list
In reply to this post by Boost - Users mailing list

> On 28 Jun 2017, at 00:53, [hidden email] wrote:
>
>
> 2017-06-27 17:46 GMT+02:00 [hidden email] <[hidden email]>:
> So I suspect this is a bug in 'type_traits' library, but I'm not sure. Hopefully somebody else can confirm. (I'm sorry but I don't know to whom should I CC this post.)
>
> I don't know why clang accepts your code though. Perhaps `boost::is_convertible` is conditionally compiled to some different implementations in Clang.
>
> Thanks for the analysis.
> My guess is that clang uses a different implementation of is_convertible.
> Also, I guess this works in C++14 because std::is_convertible is used instead?

Yes, after a deeper dig, `boost/type_traits/intrinsics.hpp` shows that `type_traits` uses Clang's intrinsics to do the job:

```
   157 #if defined(BOOST_CLANG) && defined(__has_feature) && !defined(__CUDACC__)
   [...]
   213 #   if __has_feature(is_convertible_to)
   214 #     define BOOST_IS_CONVERTIBLE(T,U) __is_convertible_to(T,U)
   215 #   endif
```

After commenting out macro `BOOST_IS_CONVERTIBLE`, clang errors out the original code as well.

And I believe this finding gives us an easier workaround: Just defining our own `BOOST_IS_CONVERTIBLE` before including `boost/variant.hpp`, and the compilation error should disappear.

```
#include <type_traits>
#define BOOST_IS_CONVERTIBLE(T,U) std::is_convertible<T, U>::value

#include <boost/variant.hpp>
[...]
```

As for the implementation of `boost::is_convertible` in C++14, although I agree with you that `type_traits` could (or should have?) use std::is_convertible when available, `boost/type_traits/is_convertible.hpp` shows that `type_traits` implemented their own C++11 version:

```
    53 //
    54
    55 namespace detail {
    56
    57 #if !defined(BOOST_NO_SFINAE_EXPR) && !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !(defined(BOOST_GCC) && (BOOST_GCC < 40700))
    58
    59   // This is a C++11 conforming version, place this first and use it wherever possible:
    60
    61 #  define BOOST_TT_CXX11_IS_CONVERTIBLE
    62
    63   template <class A, class B, class C>
    64   struct or_helper
    65   {
    66      static const bool value = (A::value || B::value || C::value);
    67   };
    68
    69   template<typename From, typename To, bool b = or_helper<boost::is_void<From>, boost::is_function<To>, boost::is_array<To> >::value>
    70   struct is_convertible_basic_impl
    71   {
    72      // Nothing converts to function or array, but void converts to void:
    73      static const bool value = is_void<To>::value;
    74   };
    75
    76   template<typename From, typename To>
    77   class is_convertible_basic_impl<From, To, false>
    78   {
    79      typedef char one;
```


_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Regression in Boost.Variant with gcc6.3 in C++03 mode

Boost - Users mailing list

> On 28 Jun 2017, at 01:27, [hidden email] wrote:
>
> And I believe this finding gives us an easier workaround: Just defining our own `BOOST_IS_CONVERTIBLE` before including `boost/variant.hpp`, and the compilation error should disappear.
>
> ```
> #include <type_traits>
> #define BOOST_IS_CONVERTIBLE(T,U) std::is_convertible<T, U>::value
>
> #include <boost/variant.hpp>
> [...]
> ```

Oh sorry I forgot you're using C++03.

Maybe you could write your own `is_convertible` in C++03 and redirect boost's to there.
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Regression in Boost.Variant with gcc6.3 in C++03 mode

Boost - Users mailing list

> On 28 Jun 2017, at 01:30, d25fe0be--- via Boost-users <[hidden email]> wrote:
>
>
>> On 28 Jun 2017, at 01:27, [hidden email] wrote:
>>
>> And I believe this finding gives us an easier workaround: Just defining our own `BOOST_IS_CONVERTIBLE` before including `boost/variant.hpp`, and the compilation error should disappear.
>>
>> ```
>> #include <type_traits>
>> #define BOOST_IS_CONVERTIBLE(T,U) std::is_convertible<T, U>::value
>>
>> #include <boost/variant.hpp>
>> [...]
>> ```
>
> Oh sorry I forgot you're using C++03.
>
> Maybe you could write your own `is_convertible` in C++03 and redirect boost's to there.

Sorry I missed John's reply..

I believe John is right, without C++11 a fully conforming is_convertible seems not to be implementable (I didn't find a way to remove `_m_check` taking the inaccessible base as a parameter from the overload set.), hence 'your own `is_convertible`' seems not to be feasible either.

Thank you John for pointing this out, and sorry for the noise.
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Regression in Boost.Variant with gcc6.3 in C++03 mode

Boost - Users mailing list


2017-06-27 20:05 GMT+02:00 [hidden email] <[hidden email]>:
Sorry I missed John's reply..

I believe John is right, without C++11 a fully conforming is_convertible seems not to be implementable (I didn't find a way to remove `_m_check` taking the inaccessible base as a parameter from the overload set.), hence 'your own `is_convertible`' seems not to be feasible either.

Thank you John for pointing this out, and sorry for the noise.

Thanks for the follow up.

So the only workaround with 1.64 is to get rid of the private inheritance, right?

Anyway, my test case used to work in older versions of Boost.
I'm not sure about what changed between 1.61 and 1.62, if it's a change in type_traits or a change in variant.
Would it be possible to recover the lost functionality in 1.65?


_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Regression in Boost.Variant with gcc6.3 in C++03 mode

Boost - Users mailing list

> On 28 Jun 2017, at 16:52, [hidden email] wrote:
>
>
>
> 2017-06-27 20:05 GMT+02:00 [hidden email] <[hidden email]>:
> Sorry I missed John's reply..
>
> I believe John is right, without C++11 a fully conforming is_convertible seems not to be implementable (I didn't find a way to remove `_m_check` taking the inaccessible base as a parameter from the overload set.), hence 'your own `is_convertible`' seems not to be feasible either.
>
> Thank you John for pointing this out, and sorry for the noise.
>
> Thanks for the follow up.
>
> So the only workaround with 1.64 is to get rid of the private inheritance, right?

I'm afraid yes.

> Anyway, my test case used to work in older versions of Boost.
> I'm not sure about what changed between 1.61 and 1.62, if it's a change in type_traits or a change in variant.
> Would it be possible to recover the lost functionality in 1.65?
>

I think the change was introduced by commit [https://github.com/boostorg/variant/commit/b3650685f941a0c35cadfd878a185f274e132788], which tried to solve ticket #11602 [https://svn.boost.org/trac10/ticket/11602].

I don't know how can both #11602 and the issue here be solved at the same time. CC'ing Antony.

Perhaps we can test for convertibility only if C++11 is available, and leave #11602 unsolved in C++03 (as there're still corner cases not solved as of now, see below)?

---

As `boost::is_constructible` triggers a hard error when testing a class and its privately parent, the following code fails the compilation as well (both before (because of #11602) and after (because of the hard error triggered by `boost::is_convertible`) commit b3650685f941a0c35cadfd878a185f274e132788):

```
#include <boost/variant.hpp>

class A {};
class B : private A {};

void foo(boost::variant<A> v) {}
void foo(boost::variant<B> v) {}

int main()
{
    B b;

    foo(b);
}
```
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Loading...