Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang] Failure when upcasting to a base class in trailing return type expression #114024

Open
carlosgalvezp opened this issue Oct 29, 2024 · 10 comments
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" diverges-from:edg Does the clang frontend diverge from edg compiler diverges-from:gcc Does the clang frontend diverge from gcc on this issue diverges-from:msvc Does the clang frontend diverge from msvc on this issue rejects-valid

Comments

@carlosgalvezp
Copy link
Contributor

carlosgalvezp commented Oct 29, 2024

Hi,

Consider this example code:

struct Base
{};

int foo(Base&);

struct Derived : Base
{
    auto f() & -> decltype(foo(static_cast<Base&>(*this)))
    {
        return foo(static_cast<Base&>(*this));
    }
};

Clang emits this failure:

<source>:9:32: error: non-const lvalue reference to type 'Base' cannot bind to a value of unrelated type 'Derived'
    9 |     auto f() & -> decltype(foo(static_cast<Base&>(*this)))
      |  

Repro.

However, GCC, MSVC and EDG accept the code. I am confused as to why Clang complains; a Derived class should be possible to upcast to its base?

Thanks!

@carlosgalvezp carlosgalvezp added clang:frontend Language frontend issues, e.g. anything involving "Sema" rejects-valid labels Oct 29, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 29, 2024

@llvm/issue-subscribers-clang-frontend

Author: Carlos Galvez (carlosgalvezp)

Hi,

Consider this example code:

struct Base
{};

int foo(Base&amp;);

struct Derived : Base
{
    auto f() &amp; -&gt; decltype(foo(static_cast&lt;Base&amp;&gt;(*this)))
    {
        return foo(static_cast&lt;Base&amp;&gt;(*this));
    }
};

Clang emits this failure:

&lt;source&gt;:9:32: error: non-const lvalue reference to type 'Base' cannot bind to a value of unrelated type 'Derived'
    9 |     auto f() &amp; -&gt; decltype(foo(static_cast&lt;Base&amp;&gt;(*this)))
      |  

Repro.

However, GCC accepts the code. I am confused as to why Clang complains; a Derived class should be possible to upcast to its base?

Thanks!

@carlosgalvezp carlosgalvezp added diverges-from:gcc Does the clang frontend diverge from gcc on this issue diverges-from:msvc Does the clang frontend diverge from msvc on this issue diverges-from:edg Does the clang frontend diverge from edg compiler labels Oct 29, 2024
@Rajveer100
Copy link
Contributor

Rajveer100 commented Oct 29, 2024

@carlosgalvezp
This way works:

auto f() & -> auto

@carlosgalvezp
Copy link
Contributor Author

carlosgalvezp commented Oct 29, 2024

@Rajveer100

decltype(auto) is unfortunately not SFINAE-friendly, so I don't believe I can use that.

@keinflue
Copy link

keinflue commented Oct 29, 2024

The return type is a not a complete-class context. Derived is not complete yet at this point and static_cast ignores inheritance-based casts if the derived class type is incomplete, even if the bases have been seen already.

@keinflue
Copy link

keinflue commented Oct 29, 2024

You do not need to cast from this though. The type and value category of the static_cast expression doesn't depend on the argument and because you already know that the cast will be valid once the class is complete, there is no SFINAE implication specific to the cast either:

auto f() & -> decltype(foo(std::declval<Base&>()))

@carlosgalvezp
Copy link
Contributor Author

Thanks, that works! Still it would be good if we could keep an exact copy of the return expression into the return type, which is a common idiom. Some people even do this via a macro to ensure the 3 places where it's needed receive the same expression.

Isn't this still a Clang issue given that all other major compilers accept the code?

@keinflue
Copy link

keinflue commented Oct 30, 2024

From what I can tell the other compilers are not standard-conforming and Clang behaves according to the standard. So fixing this would probably require a proposal to change the standard.

(But I am just an interested user, so wait for some authoritative answer.)

@carlosgalvezp
Copy link
Contributor Author

Ah, I see. Do you know in particular which section of the standard is relevant in this case?

@keinflue
Copy link

keinflue commented Oct 30, 2024

The implicit cast (which is also used by static_cast) is specified in [conv.ptr]/3. It explicitly requires the derived type to be complete.

A class is complete only after its closing } bracket (i.e. when it is "reachable", see definition in [module.reach])) per [class.mem.general]/8 except in complete-class contexts, which are listed in [class.mem.general]/7 and don't include the trailing return type.

@carlosgalvezp
Copy link
Contributor Author

Thank you for the thorough explanation!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" diverges-from:edg Does the clang frontend diverge from edg compiler diverges-from:gcc Does the clang frontend diverge from gcc on this issue diverges-from:msvc Does the clang frontend diverge from msvc on this issue rejects-valid
Projects
None yet
Development

No branches or pull requests

4 participants