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] __is_trivially_assignable returning true with deleted member #90605

Open
philnik777 opened this issue Apr 30, 2024 · 6 comments · May be fixed by #90725
Open

[Clang] __is_trivially_assignable returning true with deleted member #90605

philnik777 opened this issue Apr 30, 2024 · 6 comments · May be fixed by #90725
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" rejects-valid

Comments

@philnik777
Copy link
Contributor

philnik777 commented Apr 30, 2024

In C++03, Clang claims that a struct containing a member with a deleted assignment operator is assignable, but diagnoses the instantiation as using a deleted function.

struct Element {
    Element& operator=(const Element&) = delete;
};

struct S {
    Element i;

    S& operator=(const S&) = default;
};

_Static_assert(!__is_trivially_assignable(S&, const S&)); // assertion fails
_Static_assert(!__is_assignable(S&, const S&)); // assertion fails

void test() {
    S s;
    S s2;
    s2 = s; // diagnosed as an error
}

(Godbolt)

@philnik777 philnik777 added clang:frontend Language frontend issues, e.g. anything involving "Sema" rejects-valid labels Apr 30, 2024
@llvmbot
Copy link
Member

llvmbot commented Apr 30, 2024

@llvm/issue-subscribers-clang-frontend

Author: Nikolas Klauser (philnik777)

In C++03, Clang claims that a struct containing a member with a deleted assignment operator is assignable, but diagnoses the instantiation as using a deleted function.
struct Element {
    Element& operator=(const Element&) = delete;
};

struct S {
    Element i;

    S& operator=(const S&) = default;
};

_Static_assert(!__is_trivially_assignable(S&, const S&)); // assertion fails
_Static_assert(!__is_assignable(S&, const S&)); // assertion fails

void test() {
    S s;
    S s2;
    s2 = s; // diagnosed as an error
}

@philnik777 philnik777 changed the title [Clang] __is_trivially_assignable [Clang] __is_trivially_assignable returning true with deleted member Apr 30, 2024
@cor3ntin
Copy link
Contributor

cor3ntin commented May 1, 2024

@MitalAshok
Copy link
Contributor

@cor3ntin CWG17234 is not entirely related because it's about __is_trivially_copyable (which should be true for this class since it has a trivial copy constructor), and CWG1928 doesn't matter because __is_trivially_assignable(S&, const S&) shouldn't succeed if __is_assignable(S&, const S&) fails, and it should fail

MitalAshok added a commit to MitalAshok/llvm-project that referenced this issue May 1, 2024

Verified

This commit was signed with the committer’s verified signature.
MitalAshok Mital Ashok
…xtension in C++03

Fixes llvm#90605
@cor3ntin
Copy link
Contributor

cor3ntin commented May 1, 2024

@MitalAshok Thanks. I've read the test incorrectly initially...

@MitalAshok
Copy link
Contributor

= default used as a clang extension in C++03 makes it as if the default implicit declaration was used. In C++03, the implicit declaration is only defined when it is actually used (§12, [special], from the C++98 standard):

The implementation will implicitly declare these member functions for a class type when the program does not explicitly declare them, except as noted in 12.1. The implementation will implicitly define them if they are used, as specified in 12.1, 12.4 and 12.8.

So a smaller example:

struct X {
    X() : v(0) {}
    const int v;
    X& operator=(const X&) = default;
};

_Static_assert(__is_assignable(X&, const X&));
int main() {
    X x1;
    const X x2;
    x1 = x2;  // Error is here
}

The __is_assignable doesn't use the operator, so there is no definition. However, x1 = x2 forces a definition to be made, and the error will appear then. In other words, it's not a SFINAE error: Your class does pass __is_assignable. This is the same behaviour as if you removed S& operator=(const S&) = default; entirely.


One thing we can do is make = default; work like in C++11, where it becomes = delete; if it would be ill-formed. Or we can keep it as is, so S& operator=(const S&) = default; is the exact same as not providing any declaration. But I think we should at least document it here.

@ldionne
Copy link
Member

ldionne commented May 9, 2024

One thing we can do is make = default; work like in C++11, where it becomes = delete; if it would be ill-formed. Or we can keep it as is, so S& operator=(const S&) = default; is the exact same as not providing any declaration. But I think we should at least document it here.

I would much rather try to make this C++11-extension-in-C++03 work as closely as possible to the way it works in C++11. So my vote would go for fixing Clang so that it considers S::operator= as deleted.

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" rejects-valid
Projects
None yet
5 participants