Skip to content

Commit 2e423a7

Browse files
committed
[Clang] Added explanation why a is trivial copyable evaluated to false.
1 parent 3b6ff59 commit 2e423a7

File tree

4 files changed

+253
-1
lines changed

4 files changed

+253
-1
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1764,7 +1764,8 @@ def err_user_defined_msg_constexpr : Error<
17641764

17651765
// Type traits explanations
17661766
def note_unsatisfied_trait : Note<"%0 is not %enum_select<TraitName>{"
1767-
"%TriviallyRelocatable{trivially relocatable}"
1767+
"%TriviallyRelocatable{trivially relocatable}|"
1768+
"%TriviallyCopyable{trivially copyable}"
17681769
"}1">;
17691770

17701771
def note_unsatisfied_trait_reason
@@ -1776,6 +1777,8 @@ def note_unsatisfied_trait_reason
17761777
"%VBase{has a virtual base %1}|"
17771778
"%NRBase{has a non-trivially-relocatable base %1}|"
17781779
"%NRField{has a non-trivially-relocatable member %1 of type %2}|"
1780+
"%NTCBase{has a non-trivially-copyable base %1}|"
1781+
"%NTCField{has a non-trivially-copyable member %1 of type %2}|"
17791782
"%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
17801783
"%UserProvidedCtr{has a user provided %select{copy|move}1 "
17811784
"constructor}|"

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "clang/AST/DeclCXX.h"
14+
#include "clang/AST/Type.h"
1415
#include "clang/Basic/DiagnosticParse.h"
1516
#include "clang/Basic/DiagnosticSema.h"
17+
#include "clang/Basic/TypeTraits.h"
1618
#include "clang/Sema/EnterExpressionEvaluationContext.h"
1719
#include "clang/Sema/Initialization.h"
1820
#include "clang/Sema/Lookup.h"
@@ -1922,6 +1924,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
19221924
return llvm::StringSwitch<std::optional<TypeTrait>>(Name)
19231925
.Case("is_trivially_relocatable",
19241926
TypeTrait::UTT_IsCppTriviallyRelocatable)
1927+
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
19251928
.Default(std::nullopt);
19261929
}
19271930

@@ -2083,6 +2086,97 @@ static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
20832086
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
20842087
}
20852088

2089+
static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
2090+
SourceLocation Loc,
2091+
const CXXRecordDecl *D) {
2092+
for (const CXXBaseSpecifier &B : D->bases()) {
2093+
assert(B.getType()->getAsCXXRecordDecl() && "invalid base?");
2094+
if (B.isVirtual())
2095+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2096+
<< diag::TraitNotSatisfiedReason::VBase << B.getType()
2097+
<< B.getSourceRange();
2098+
if (!B.getType().isTriviallyCopyableType(D->getASTContext())) {
2099+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2100+
<< diag::TraitNotSatisfiedReason::NTCBase << B.getType()
2101+
<< B.getSourceRange();
2102+
}
2103+
}
2104+
for (const FieldDecl *Field : D->fields()) {
2105+
if (!Field->getType().isTriviallyCopyableType(Field->getASTContext()))
2106+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2107+
<< diag::TraitNotSatisfiedReason::NTCField << Field
2108+
<< Field->getType() << Field->getSourceRange();
2109+
}
2110+
if (D->hasDeletedDestructor())
2111+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2112+
<< diag::TraitNotSatisfiedReason::DeletedDtr << 0
2113+
<< D->getDestructor()->getSourceRange();
2114+
2115+
if (D->isUnion()) {
2116+
auto DiagSPM = [&](CXXSpecialMemberKind K, bool Has) {
2117+
if (Has)
2118+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2119+
<< diag::TraitNotSatisfiedReason::UnionWithUserDeclaredSMF << K;
2120+
};
2121+
DiagSPM(CXXSpecialMemberKind::CopyConstructor,
2122+
D->hasUserDeclaredCopyConstructor());
2123+
DiagSPM(CXXSpecialMemberKind::CopyAssignment,
2124+
D->hasUserDeclaredCopyAssignment());
2125+
DiagSPM(CXXSpecialMemberKind::MoveConstructor,
2126+
D->hasUserDeclaredMoveConstructor());
2127+
DiagSPM(CXXSpecialMemberKind::MoveAssignment,
2128+
D->hasUserDeclaredMoveAssignment());
2129+
return;
2130+
}
2131+
2132+
if (!D->hasSimpleMoveConstructor() && !D->hasSimpleCopyConstructor()) {
2133+
const auto *Decl = cast<CXXConstructorDecl>(
2134+
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false));
2135+
if (Decl && Decl->isUserProvided())
2136+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2137+
<< diag::TraitNotSatisfiedReason::UserProvidedCtr
2138+
<< Decl->isMoveConstructor() << Decl->getSourceRange();
2139+
}
2140+
if (!D->hasSimpleMoveAssignment() && !D->hasSimpleCopyAssignment()) {
2141+
CXXMethodDecl *Decl =
2142+
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
2143+
if (Decl && Decl->isUserProvided())
2144+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2145+
<< diag::TraitNotSatisfiedReason::UserProvidedAssign
2146+
<< Decl->isMoveAssignmentOperator() << Decl->getSourceRange();
2147+
}
2148+
CXXDestructorDecl *Dtr = D->getDestructor();
2149+
if (Dtr && Dtr->isUserProvided() && !Dtr->isDefaulted())
2150+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2151+
<< diag::TraitNotSatisfiedReason::DeletedDtr << 1
2152+
<< Dtr->getSourceRange();
2153+
}
2154+
2155+
static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
2156+
SourceLocation Loc, QualType T) {
2157+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
2158+
<< T << diag::TraitName::TriviallyCopyable;
2159+
2160+
if (T->isReferenceType())
2161+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2162+
<< diag::TraitNotSatisfiedReason::Ref;
2163+
2164+
T = T.getNonReferenceType();
2165+
2166+
if (T.hasNonTrivialObjCLifetime())
2167+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2168+
<< diag::TraitNotSatisfiedReason::HasArcLifetime;
2169+
2170+
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
2171+
if (!D || D->isInvalidDecl())
2172+
return;
2173+
2174+
if (D->hasDefinition())
2175+
DiagnoseNonTriviallyCopyableReason(SemaRef, Loc, D);
2176+
2177+
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
2178+
}
2179+
20862180
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
20872181
E = E->IgnoreParenImpCasts();
20882182
if (E->containsErrors())
@@ -2097,6 +2191,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
20972191
case UTT_IsCppTriviallyRelocatable:
20982192
DiagnoseNonTriviallyRelocatableReason(*this, E->getBeginLoc(), Args[0]);
20992193
break;
2194+
case UTT_IsTriviallyCopyable:
2195+
DiagnoseNonTriviallyCopyableReason(*this, E->getBeginLoc(), Args[0]);
2196+
break;
21002197
default:
21012198
break;
21022199
}

clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ struct is_trivially_relocatable {
1212

1313
template <typename T>
1414
constexpr bool is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T);
15+
16+
template <typename T>
17+
struct is_trivially_copyable {
18+
static constexpr bool value = __is_trivially_copyable(T);
19+
};
20+
21+
template <typename T>
22+
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
1523
#endif
1624

1725
#ifdef STD2
@@ -25,6 +33,17 @@ using is_trivially_relocatable = __details_is_trivially_relocatable<T>;
2533

2634
template <typename T>
2735
constexpr bool is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T);
36+
37+
template <typename T>
38+
struct __details_is_trivially_copyable {
39+
static constexpr bool value = __is_trivially_copyable(T);
40+
};
41+
42+
template <typename T>
43+
using is_trivially_copyable = __details_is_trivially_copyable<T>;
44+
45+
template <typename T>
46+
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
2847
#endif
2948

3049

@@ -45,6 +64,15 @@ using is_trivially_relocatable = __details_is_trivially_relocatable<T>;
4564

4665
template <typename T>
4766
constexpr bool is_trivially_relocatable_v = is_trivially_relocatable<T>::value;
67+
68+
template <typename T>
69+
struct __details_is_trivially_copyable : bool_constant<__is_trivially_copyable(T)> {};
70+
71+
template <typename T>
72+
using is_trivially_copyable = __details_is_trivially_copyable<T>;
73+
74+
template <typename T>
75+
constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
4876
#endif
4977

5078
}
@@ -60,6 +88,18 @@ static_assert(std::is_trivially_relocatable_v<int&>);
6088
// expected-note@-1 {{'int &' is not trivially relocatable}} \
6189
// expected-note@-1 {{because it is a reference type}}
6290

91+
static_assert(std::is_trivially_copyable<int>::value);
92+
93+
static_assert(std::is_trivially_copyable<int&>::value);
94+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_trivially_copyable<int &>::value'}} \
95+
// expected-note@-1 {{'int &' is not trivially copyable}} \
96+
// expected-note@-1 {{because it is a reference type}}
97+
static_assert(std::is_trivially_copyable_v<int&>);
98+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_trivially_copyable_v<int &>'}} \
99+
// expected-note@-1 {{'int &' is not trivially copyable}} \
100+
// expected-note@-1 {{because it is a reference type}}
101+
102+
63103
namespace test_namespace {
64104
using namespace std;
65105
static_assert(is_trivially_relocatable<int&>::value);
@@ -70,6 +110,15 @@ namespace test_namespace {
70110
// expected-error@-1 {{static assertion failed due to requirement 'is_trivially_relocatable_v<int &>'}} \
71111
// expected-note@-1 {{'int &' is not trivially relocatable}} \
72112
// expected-note@-1 {{because it is a reference type}}
113+
114+
static_assert(is_trivially_copyable<int&>::value);
115+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_trivially_copyable<int &>::value'}} \
116+
// expected-note@-1 {{'int &' is not trivially copyable}} \
117+
// expected-note@-1 {{because it is a reference type}}
118+
static_assert(is_trivially_copyable_v<int&>);
119+
// expected-error@-1 {{static assertion failed due to requirement 'is_trivially_copyable_v<int &>'}} \
120+
// expected-note@-1 {{'int &' is not trivially copyable}} \
121+
// expected-note@-1 {{because it is a reference type}}
73122
}
74123

75124

@@ -82,6 +131,14 @@ concept C = std::is_trivially_relocatable_v<T>; // #concept2
82131

83132
template <C T> void g(); // #cand2
84133

134+
template <typename T>
135+
requires std::is_trivially_copyable<T>::value void f2(); // #cand3
136+
137+
template <typename T>
138+
concept C2 = std::is_trivially_copyable_v<T>; // #concept4
139+
140+
template <C2 T> void g2(); // #cand4
141+
85142
void test() {
86143
f<int&>();
87144
// expected-error@-1 {{no matching function for call to 'f'}} \
@@ -97,5 +154,20 @@ void test() {
97154
// expected-note@#concept2 {{because 'std::is_trivially_relocatable_v<int &>' evaluated to false}} \
98155
// expected-note@#concept2 {{'int &' is not trivially relocatable}} \
99156
// expected-note@#concept2 {{because it is a reference type}}
157+
158+
f2<int&>();
159+
// expected-error@-1 {{no matching function for call to 'f2'}} \
160+
// expected-note@#cand3 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
161+
// expected-note-re@#cand3 {{because '{{.*}}is_trivially_copyable<int &>::value' evaluated to false}} \
162+
// expected-note@#cand3 {{'int &' is not trivially copyable}} \
163+
// expected-note@#cand3 {{because it is a reference type}}
164+
165+
g2<int&>();
166+
// expected-error@-1 {{no matching function for call to 'g2'}} \
167+
// expected-note@#cand4 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
168+
// expected-note@#cand4 {{because 'int &' does not satisfy 'C2'}} \
169+
// expected-note@#concept4 {{because 'std::is_trivially_copyable_v<int &>' evaluated to false}} \
170+
// expected-note@#concept4 {{'int &' is not trivially copyable}} \
171+
// expected-note@#concept4 {{because it is a reference type}}
100172
}
101173
}

clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,83 @@ static_assert(__builtin_is_cpp_trivially_relocatable(U2));
144144
// expected-note@#tr-U2 {{'U2' defined here}}
145145

146146
}
147+
148+
namespace trivially_copyable {
149+
struct B {
150+
virtual ~B();
151+
};
152+
struct S : virtual B { // #tc-S
153+
S();
154+
int & a;
155+
const int ci;
156+
B & b;
157+
B c;
158+
~S();
159+
};
160+
static_assert(__is_trivially_copyable(S));
161+
// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::S)'}} \
162+
// expected-note@-1 {{'S' is not trivially copyable}} \
163+
// expected-note@-1 {{because it has a virtual base 'B'}} \
164+
// expected-note@-1 {{because it has a non-trivially-copyable base 'B'}} \
165+
// expected-note@-1 {{because it has a non-trivially-copyable member 'c' of type 'B'}} \
166+
// expected-note@-1 {{because it has a non-trivially-copyable member 'b' of type 'B &'}} \
167+
// expected-note@-1 {{because it has a non-trivially-copyable member 'a' of type 'int &'}} \
168+
// expected-note@-1 {{because it has a user-provided destructor}}
169+
// expected-note@#tc-S {{'S' defined here}}
170+
171+
struct S2 { // #tc-S2
172+
S2(S2&&);
173+
S2& operator=(const S2&);
174+
};
175+
static_assert(__is_trivially_copyable(S2));
176+
// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::S2)'}} \
177+
// expected-note@-1 {{'S2' is not trivially copyable}} \
178+
// expected-note@-1 {{because it has a user provided move constructor}} \
179+
// expected-note@-1 {{because it has a user provided copy assignment operator}} \
180+
// expected-note@#tc-S2 {{'S2' defined here}}
181+
182+
struct S3 {
183+
~S3() = delete;
184+
};
185+
static_assert(__is_trivially_copyable(S3));
186+
187+
union U { // #tc-U
188+
U(const U&);
189+
U(U&&);
190+
U& operator=(const U&);
191+
U& operator=(U&&);
192+
};
193+
static_assert(__is_trivially_copyable(U));
194+
// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U)'}} \
195+
// expected-note@-1 {{'U' is not trivially copyable}} \
196+
// expected-note@-1 {{because it is a union with a user-declared copy constructor}} \
197+
// expected-note@-1 {{because it is a union with a user-declared copy assignment operator}} \
198+
// expected-note@-1 {{because it is a union with a user-declared move constructor}} \
199+
// expected-note@-1 {{because it is a union with a user-declared move assignment operator}}
200+
// expected-note@#tc-U {{'U' defined here}}
201+
202+
struct S4 { // #tc-S4
203+
~S4();
204+
B b;
205+
};
206+
static_assert(__is_trivially_copyable(S4));
207+
// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::S4)'}} \
208+
// expected-note@-1 {{'S4' is not trivially copyable}} \
209+
// expected-note@-1 {{because it has a non-trivially-copyable member 'b' of type 'B'}} \
210+
// expected-note@-1 {{because it has a user-provided destructor}} \
211+
// expected-note@#tc-S4 {{'S4' defined here}}
212+
213+
union U2 { // #tc-U2
214+
U2(const U2&);
215+
U2(U2&&);
216+
B b;
217+
};
218+
static_assert(__is_trivially_copyable(U2));
219+
// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_copyable(trivially_copyable::U2)'}} \
220+
// expected-note@-1 {{'U2' is not trivially copyable}} \
221+
// expected-note@-1 {{because it is a union with a user-declared copy constructor}} \
222+
// expected-note@-1 {{because it is a union with a user-declared move constructor}} \
223+
// expected-note@-1 {{because it has a deleted destructor}} \
224+
// expected-note@-1 {{because it has a non-trivially-copyable member 'b' of type 'B'}} \
225+
// expected-note@#tc-U2 {{'U2' defined here}}
226+
}

0 commit comments

Comments
 (0)