Opened 10 years ago

Closed 4 years ago

#7251 closed Patches (obsolete)

is_convertible works incorrectly for rvalue source types

Reported by: Michel Morin Owned by: John Maddock
Milestone: To Be Determined Component: type_traits
Version: Boost Development Trunk Severity: Problem
Keywords: Cc:

Description

As reported by John Bytheway at boost-devel ML, non-intrinsic version of is_convertible<From, To> works incorrectly when From is rvalues. The current code incorrectly handles From as an lvalue, even if it is an rvalue.

Attachments (1)

fix_is_convertible_for_rvalues_cxx11.patch (7.6 KB ) - added by Michel Morin 10 years ago.
Patch for C++11

Download all attachments as: .zip

Change History (8)

by Michel Morin, 10 years ago

Patch for C++11

comment:1 by Michel Morin, 10 years ago

Attached a patch for C++11.

comment:2 by John Maddock, 10 years ago

(In [80234]) Move new test cases into the "tricky" section as many compilers can't handle them. Revert previous change that used std::is_convertible on GCC as it breaks code prior to gcc-4.7. Fix MSVC intrinsic to handle function types better. Filter out rvalue-refs from is_function. Partially apply patch from #7251 to make is_convertible behave more according to C++11. Refs #7251.

comment:3 by John Maddock, 10 years ago

Michel,

I've moved the extra test cases you added into the tricky*.cpp files as more than a few compilers fail these cases (once the main test file stops building, then we're no longer testing anything at all on that compiler, so I'd prefer to keep troublesome test cases separate).

I've applied the patch, but restricted it's application to GCC to versions 4.7 and later - earlier versions don't build the test suite with the patch applied :-( Versions 4.5.x and 4.6.x fail to build with function types, while version 4.4.x fails with just about everything.

It's possible that an extra #if branch for SFINAE-expressions could fix the remaining issues, but equally it might just break more stuff :-(

Cheers, John.

comment:4 by Michel Morin, 10 years ago

Hi John,

Thanks for looking into the problem.

I've applied the patch, but restricted it's application to GCC to versions 4.7 and later - earlier versions don't build the test suite with the patch applied :-
(Versions 4.5.x and 4.6.x fail to build with function types,

gcc 4.3-4.6 implements C++0x drafts, and there are some inconsistencies with C++11. For example,

  • gcc 4.3-4.4: rvalue references can be bound to lvalues.
  • gcc 4.5-4.6: rvalue references to function type is not an lvalue. So, for a function type Func, Func& cannot be bound to Func&&. Accordingly, is_convertible<Func, Func&> returns different results than C++11 and this causes the test failures.


while version 4.4.x fails with just about everything.

It's possible that an extra #if branch for SFINAE-expressions could fix the remaining issues, but equally it might just break more stuff :-(

Since T is not a dependent type in_m_check, SFINAE is not related. Indeed, the errors on gcc 4.3-4.4 can be reproduced without using templates. (And note that gcc 4.4 supports SFINAE for expressions.) I guess that the reason for the test failures on gcc 4.3-4.4 is a compiler's bug related to rvalue references.

#if branch is not needed for BOOST_NO_SFINAE_EXPR, but is needed for __GNUC__ == 4 && (__GNUC_MINOR__ == 3 || __GNUC_MINOR__ == 4).

comment:5 by Michel Morin, 10 years ago

On gcc 4.5-4.6 in a C++11 mode, Func&& cannot be bound to const Func&. This is because,

  • An rvalue reference to function type is an rvalue, on these compilers.
  • Function type cannot be cv-qualified.

So, on these compilers, any_conversion needs constructors taking rvalue references:

struct any_conversion
{
#ifndef BOOST_NO_RVALUE_REFERENCES    
    template <typename T> any_conversion(T&&);
#else
    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&);
#endif
};

Or maybe the following is enough:

struct any_conversion
{
#ifndef BOOST_NO_RVALUE_REFERENCES    
    template <typename T> any_conversion(T&&);
#else
    template <typename T> any_conversion(T&);
#endif
};

I'm planning to make a patch which enables the recently introduced is_convertible code on gcc 4.5-4.6 with the above any_conversion modification. Does it make sense?

comment:6 by Michel Morin, 10 years ago

I have some questions.

First, here is the description of is_convertible from the C++11 Standard:

Given the following function prototype:

template <class T>
typename add_rvalue_reference<T>::type create();

the predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and only if the following code would be well-formed, including any implicit conversions to the return type of the function:

To test() {
    return create<From>();
}


In C++11, for a function type Func, is_convertible<Func, Func&> returns true_type. But, on gcc 4.5-4.6, Func&& cannot be convertible to Func&. My question is "What should is_convertible<Func, Func&> return on gcc 4.5-4.6?"

Also, in C++03, what should is_convertible<Func, Func*> return? It would be reasonable to return true_type, since is_convertible<Func, Func*> returns true_type in C++11. But we can make another interpretation. Boost's interpretation of C++03 add_rvalue_reference<T> is just returning T. Accordingly, declval<T>() returns a value of T. So,

template <class T>
typename add_rvalue_reference<T>::type create();

To test() {
    return create<From>();
}

is ill-formed when From is a function type. With this interpretation, is_convertible<Func, Func*> should return false_type.

comment:7 by John Maddock, 4 years ago

Resolution: obsolete
Status: newclosed

I believe this to be essentially obsolete now, please file a new issue on Github if not.

Note: See TracTickets for help on using tickets.