Skip to content

Commit a661d61

Browse files
cor3ntintomtor
authored andcommitted
[Clang] Explain why a type is not replaceable. (llvm#143265)
As a drive by fix the definition of replaceable, that did not correctly implement https://eel.is/c++draft/class.prop#6.3
1 parent e4287b9 commit a661d61

File tree

5 files changed

+321
-35
lines changed

5 files changed

+321
-35
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,27 +1763,39 @@ def err_user_defined_msg_constexpr : Error<
17631763
"constant expression">;
17641764

17651765
// Type traits explanations
1766-
def note_unsatisfied_trait : Note<"%0 is not %enum_select<TraitName>{"
1767-
"%TriviallyRelocatable{trivially relocatable}|"
1768-
"%TriviallyCopyable{trivially copyable}"
1769-
"}1">;
1766+
def note_unsatisfied_trait
1767+
: Note<"%0 is not %enum_select<TraitName>{"
1768+
"%TriviallyRelocatable{trivially relocatable}|"
1769+
"%Replaceable{replaceable}|"
1770+
"%TriviallyCopyable{trivially copyable}"
1771+
"}1">;
17701772

17711773
def note_unsatisfied_trait_reason
17721774
: Note<"because it "
17731775
"%enum_select<TraitNotSatisfiedReason>{"
17741776
"%Ref{is a reference type}|"
1777+
"%Const{is const}|"
1778+
"%Volatile{is volatile}|"
17751779
"%HasArcLifetime{has an ARC lifetime qualifier}|"
17761780
"%VLA{is a variably-modified type}|"
17771781
"%VBase{has a virtual base %1}|"
1782+
"%NotScalarOrClass{not %select{a|an array of objects of}1 scalar or "
1783+
"class type}|"
17781784
"%NTRBase{has a non-trivially-relocatable base %1}|"
17791785
"%NTRField{has a non-trivially-relocatable member %1 of type %2}|"
1786+
"%NonReplaceableBase{has a non-replaceable base %1}|"
1787+
"%NonReplaceableField{has a non-replaceable member %1 of type %2}|"
17801788
"%NTCBase{has a non-trivially-copyable base %1}|"
17811789
"%NTCField{has a non-trivially-copyable member %1 of type %2}|"
17821790
"%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
17831791
"%UserProvidedCtr{has a user provided %select{copy|move}1 "
17841792
"constructor}|"
1793+
"%DeletedCtr{has a deleted %select{copy|move}1 "
1794+
"constructor}|"
17851795
"%UserProvidedAssign{has a user provided %select{copy|move}1 "
17861796
"assignment operator}|"
1797+
"%DeletedAssign{has a deleted %select{copy|move}1 "
1798+
"assignment operator}|"
17871799
"%UnionWithUserDeclaredSMF{is a union with a user-declared "
17881800
"%sub{select_special_member_kind}1}"
17891801
"}0">;

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 128 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ static CXXMethodDecl *LookupSpecialMemberFromXValue(Sema &SemaRef,
104104
OverloadCandidateSet::iterator Best;
105105
switch (OCS.BestViableFunction(SemaRef, LookupLoc, Best)) {
106106
case OR_Success:
107+
case OR_Deleted:
107108
return cast<CXXMethodDecl>(Best->Function);
108109
default:
109110
return nullptr;
@@ -120,7 +121,8 @@ static bool hasSuitableConstructorForRelocation(Sema &SemaRef,
120121

121122
CXXMethodDecl *Decl =
122123
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false);
123-
return Decl && Decl->isUserProvided() == AllowUserDefined;
124+
return Decl && Decl->isUserProvided() == AllowUserDefined &&
125+
!Decl->isDeleted();
124126
}
125127

126128
static bool hasSuitableMoveAssignmentOperatorForRelocation(
@@ -135,7 +137,8 @@ static bool hasSuitableMoveAssignmentOperatorForRelocation(
135137
if (!Decl)
136138
return false;
137139

138-
return Decl && Decl->isUserProvided() == AllowUserDefined;
140+
return Decl && Decl->isUserProvided() == AllowUserDefined &&
141+
!Decl->isDeleted();
139142
}
140143

141144
// [C++26][class.prop]
@@ -1940,6 +1943,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
19401943
return llvm::StringSwitch<std::optional<TypeTrait>>(Name)
19411944
.Case("is_trivially_relocatable",
19421945
TypeTrait::UTT_IsCppTriviallyRelocatable)
1946+
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
19431947
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
19441948
.Default(std::nullopt);
19451949
}
@@ -2005,35 +2009,8 @@ static ExtractedTypeTraitInfo ExtractTypeTraitFromExpression(const Expr *E) {
20052009
return std::nullopt;
20062010
}
20072011

2008-
static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
2009-
SourceLocation Loc,
2010-
const CXXRecordDecl *D) {
2011-
for (const CXXBaseSpecifier &B : D->bases()) {
2012-
assert(B.getType()->getAsCXXRecordDecl() && "invalid base?");
2013-
if (B.isVirtual())
2014-
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2015-
<< diag::TraitNotSatisfiedReason::VBase << B.getType()
2016-
<< B.getSourceRange();
2017-
if (!SemaRef.IsCXXTriviallyRelocatableType(B.getType()))
2018-
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2019-
<< diag::TraitNotSatisfiedReason::NTRBase << B.getType()
2020-
<< B.getSourceRange();
2021-
}
2022-
for (const FieldDecl *Field : D->fields()) {
2023-
if (!Field->getType()->isReferenceType() &&
2024-
!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
2025-
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2026-
<< diag::TraitNotSatisfiedReason::NTRField << Field
2027-
<< Field->getType() << Field->getSourceRange();
2028-
}
2029-
if (D->hasDeletedDestructor())
2030-
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2031-
<< diag::TraitNotSatisfiedReason::DeletedDtr << /*Deleted*/ 0
2032-
<< D->getDestructor()->getSourceRange();
2033-
2034-
if (D->hasAttr<TriviallyRelocatableAttr>())
2035-
return;
2036-
2012+
static void DiagnoseNonDefaultMovable(Sema &SemaRef, SourceLocation Loc,
2013+
const CXXRecordDecl *D) {
20372014
if (D->isUnion()) {
20382015
auto DiagSPM = [&](CXXSpecialMemberKind K, bool Has) {
20392016
if (Has)
@@ -2074,6 +2051,37 @@ static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
20742051
<< Dtr->getSourceRange();
20752052
}
20762053

2054+
static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
2055+
SourceLocation Loc,
2056+
const CXXRecordDecl *D) {
2057+
for (const CXXBaseSpecifier &B : D->bases()) {
2058+
assert(B.getType()->getAsCXXRecordDecl() && "invalid base?");
2059+
if (B.isVirtual())
2060+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2061+
<< diag::TraitNotSatisfiedReason::VBase << B.getType()
2062+
<< B.getSourceRange();
2063+
if (!SemaRef.IsCXXTriviallyRelocatableType(B.getType()))
2064+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2065+
<< diag::TraitNotSatisfiedReason::NTRBase << B.getType()
2066+
<< B.getSourceRange();
2067+
}
2068+
for (const FieldDecl *Field : D->fields()) {
2069+
if (!Field->getType()->isReferenceType() &&
2070+
!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
2071+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2072+
<< diag::TraitNotSatisfiedReason::NTRField << Field
2073+
<< Field->getType() << Field->getSourceRange();
2074+
}
2075+
if (D->hasDeletedDestructor())
2076+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2077+
<< diag::TraitNotSatisfiedReason::DeletedDtr << /*Deleted*/ 0
2078+
<< D->getDestructor()->getSourceRange();
2079+
2080+
if (D->hasAttr<TriviallyRelocatableAttr>())
2081+
return;
2082+
DiagnoseNonDefaultMovable(SemaRef, Loc, D);
2083+
}
2084+
20772085
static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
20782086
SourceLocation Loc,
20792087
QualType T) {
@@ -2102,6 +2110,92 @@ static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
21022110
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
21032111
}
21042112

2113+
static void DiagnoseNonReplaceableReason(Sema &SemaRef, SourceLocation Loc,
2114+
const CXXRecordDecl *D) {
2115+
for (const CXXBaseSpecifier &B : D->bases()) {
2116+
assert(B.getType()->getAsCXXRecordDecl() && "invalid base?");
2117+
if (!SemaRef.IsCXXReplaceableType(B.getType()))
2118+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2119+
<< diag::TraitNotSatisfiedReason::NonReplaceableBase << B.getType()
2120+
<< B.getSourceRange();
2121+
}
2122+
for (const FieldDecl *Field : D->fields()) {
2123+
if (!SemaRef.IsCXXReplaceableType(Field->getType()))
2124+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2125+
<< diag::TraitNotSatisfiedReason::NonReplaceableField << Field
2126+
<< Field->getType() << Field->getSourceRange();
2127+
}
2128+
if (D->hasDeletedDestructor())
2129+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2130+
<< diag::TraitNotSatisfiedReason::DeletedDtr << /*Deleted*/ 0
2131+
<< D->getDestructor()->getSourceRange();
2132+
2133+
if (!D->hasSimpleMoveConstructor() && !D->hasSimpleCopyConstructor()) {
2134+
const auto *Decl = cast<CXXConstructorDecl>(
2135+
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false));
2136+
if (Decl && Decl->isDeleted())
2137+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2138+
<< diag::TraitNotSatisfiedReason::DeletedCtr
2139+
<< Decl->isMoveConstructor() << Decl->getSourceRange();
2140+
}
2141+
if (!D->hasSimpleMoveAssignment() && !D->hasSimpleCopyAssignment()) {
2142+
CXXMethodDecl *Decl =
2143+
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
2144+
if (Decl && Decl->isDeleted())
2145+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2146+
<< diag::TraitNotSatisfiedReason::DeletedAssign
2147+
<< Decl->isMoveAssignmentOperator() << Decl->getSourceRange();
2148+
}
2149+
2150+
if (D->hasAttr<ReplaceableAttr>())
2151+
return;
2152+
DiagnoseNonDefaultMovable(SemaRef, Loc, D);
2153+
}
2154+
2155+
static void DiagnoseNonReplaceableReason(Sema &SemaRef, SourceLocation Loc,
2156+
QualType T) {
2157+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
2158+
<< T << diag::TraitName::Replaceable;
2159+
2160+
if (T->isVariablyModifiedType())
2161+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2162+
<< diag::TraitNotSatisfiedReason::VLA;
2163+
2164+
if (T->isReferenceType())
2165+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2166+
<< diag::TraitNotSatisfiedReason::Ref;
2167+
T = T.getNonReferenceType();
2168+
2169+
if (T.isConstQualified())
2170+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2171+
<< diag::TraitNotSatisfiedReason::Const;
2172+
2173+
if (T.isVolatileQualified())
2174+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2175+
<< diag::TraitNotSatisfiedReason::Volatile;
2176+
2177+
bool IsArray = T->isArrayType();
2178+
T = SemaRef.getASTContext().getBaseElementType(T.getUnqualifiedType());
2179+
2180+
if (T->isScalarType())
2181+
return;
2182+
2183+
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
2184+
if (!D) {
2185+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2186+
<< diag::TraitNotSatisfiedReason::NotScalarOrClass << IsArray;
2187+
return;
2188+
}
2189+
2190+
if (D->isInvalidDecl())
2191+
return;
2192+
2193+
if (D->hasDefinition())
2194+
DiagnoseNonReplaceableReason(SemaRef, Loc, D);
2195+
2196+
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
2197+
}
2198+
21052199
static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
21062200
SourceLocation Loc,
21072201
const CXXRecordDecl *D) {
@@ -2192,6 +2286,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
21922286
case UTT_IsCppTriviallyRelocatable:
21932287
DiagnoseNonTriviallyRelocatableReason(*this, E->getBeginLoc(), Args[0]);
21942288
break;
2289+
case UTT_IsReplaceable:
2290+
DiagnoseNonReplaceableReason(*this, E->getBeginLoc(), Args[0]);
2291+
break;
21952292
case UTT_IsTriviallyCopyable:
21962293
DiagnoseNonTriviallyCopyableReason(*this, E->getBeginLoc(), Args[0]);
21972294
break;

clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,31 @@ struct CopyAssign1 {
333333
CopyAssign1 & operator=(CopyAssign1 const &) = default;
334334
};
335335

336+
struct UserDeleted1 {
337+
UserDeleted1(const UserDeleted1&) = delete;
338+
};
339+
static_assert(!__builtin_is_cpp_trivially_relocatable(UserDeleted1));
340+
static_assert(!__builtin_is_replaceable(UserDeleted1));
341+
342+
struct UserDeleted2 {
343+
UserDeleted2(UserDeleted2&&) = delete;
344+
};
345+
static_assert(!__builtin_is_cpp_trivially_relocatable(UserDeleted2));
346+
static_assert(!__builtin_is_replaceable(UserDeleted2));
347+
348+
349+
struct UserDeleted3 {
350+
UserDeleted3 operator=(UserDeleted3);
351+
};
352+
static_assert(!__builtin_is_cpp_trivially_relocatable(UserDeleted3));
353+
static_assert(!__builtin_is_replaceable(UserDeleted3));
354+
355+
struct UserDeleted4 {
356+
UserDeleted4 operator=(UserDeleted4&&);
357+
};
358+
static_assert(!__builtin_is_cpp_trivially_relocatable(UserDeleted4));
359+
static_assert(!__builtin_is_replaceable(UserDeleted4));
360+
336361
}
337362

338363

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,24 @@ void test() {
171171
// expected-note@#concept4 {{because it is a reference type}}
172172
}
173173
}
174+
175+
176+
namespace std {
177+
template <typename T>
178+
struct is_replaceable {
179+
static constexpr bool value = __builtin_is_replaceable(T);
180+
};
181+
182+
template <typename T>
183+
constexpr bool is_replaceable_v = __builtin_is_replaceable(T);
184+
185+
}
186+
187+
static_assert(std::is_replaceable<int&>::value);
188+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_replaceable<int &>::value'}} \
189+
// expected-note@-1 {{'int &' is not replaceable}} \
190+
// expected-note@-1 {{because it is a reference type}}
191+
static_assert(std::is_replaceable_v<int&>);
192+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_replaceable_v<int &>'}} \
193+
// expected-note@-1 {{'int &' is not replaceable}} \
194+
// expected-note@-1 {{because it is a reference type}}

0 commit comments

Comments
 (0)