Skip to content

Commit 3ef796b

Browse files
authored
[Clang] Diagnose unsatisfied std::is_assignable. (#144836)
Part of the work for #141911. Checking `is_assignable<T, U>` boils down to checking the well-formedness of `declval<T>() = declval<U>()`; this PR recycles logic from EvaluateBinaryTypeTrait in order to produce the relevant diagnostics.
1 parent d3ed84e commit 3ef796b

File tree

3 files changed

+174
-8
lines changed

3 files changed

+174
-8
lines changed

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,14 +1725,15 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT,
17251725

17261726
// Build expressions that emulate the effect of declval<T>() and
17271727
// declval<U>().
1728-
if (LhsT->isObjectType() || LhsT->isFunctionType())
1729-
LhsT = Self.Context.getRValueReferenceType(LhsT);
1730-
if (RhsT->isObjectType() || RhsT->isFunctionType())
1731-
RhsT = Self.Context.getRValueReferenceType(RhsT);
1732-
OpaqueValueExpr Lhs(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
1733-
Expr::getValueKindForType(LhsT));
1734-
OpaqueValueExpr Rhs(KeyLoc, RhsT.getNonLValueExprType(Self.Context),
1735-
Expr::getValueKindForType(RhsT));
1728+
auto createDeclValExpr = [&](QualType Ty) -> OpaqueValueExpr {
1729+
if (Ty->isObjectType() || Ty->isFunctionType())
1730+
Ty = Self.Context.getRValueReferenceType(Ty);
1731+
return {KeyLoc, Ty.getNonLValueExprType(Self.Context),
1732+
Expr::getValueKindForType(Ty)};
1733+
};
1734+
1735+
auto Lhs = createDeclValExpr(LhsT);
1736+
auto Rhs = createDeclValExpr(RhsT);
17361737

17371738
// Attempt the assignment in an unevaluated context within a SFINAE
17381739
// trap at translation unit scope.
@@ -1956,6 +1957,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
19561957
TypeTrait::UTT_IsCppTriviallyRelocatable)
19571958
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
19581959
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
1960+
.Case("is_assignable", TypeTrait::BTT_IsAssignable)
19591961
.Default(std::nullopt);
19601962
}
19611963

@@ -2285,6 +2287,32 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
22852287
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
22862288
}
22872289

2290+
static void DiagnoseNonAssignableReason(Sema &SemaRef, SourceLocation Loc,
2291+
QualType T, QualType U) {
2292+
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
2293+
2294+
auto createDeclValExpr = [&](QualType Ty) -> OpaqueValueExpr {
2295+
if (Ty->isObjectType() || Ty->isFunctionType())
2296+
Ty = SemaRef.Context.getRValueReferenceType(Ty);
2297+
return {Loc, Ty.getNonLValueExprType(SemaRef.Context),
2298+
Expr::getValueKindForType(Ty)};
2299+
};
2300+
2301+
auto LHS = createDeclValExpr(T);
2302+
auto RHS = createDeclValExpr(U);
2303+
2304+
EnterExpressionEvaluationContext Unevaluated(
2305+
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
2306+
Sema::ContextRAII TUContext(SemaRef,
2307+
SemaRef.Context.getTranslationUnitDecl());
2308+
SemaRef.BuildBinOp(/*S=*/nullptr, Loc, BO_Assign, &LHS, &RHS);
2309+
2310+
if (!D || D->isInvalidDecl())
2311+
return;
2312+
2313+
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
2314+
}
2315+
22882316
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
22892317
E = E->IgnoreParenImpCasts();
22902318
if (E->containsErrors())
@@ -2305,6 +2333,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
23052333
case UTT_IsTriviallyCopyable:
23062334
DiagnoseNonTriviallyCopyableReason(*this, E->getBeginLoc(), Args[0]);
23072335
break;
2336+
case BTT_IsAssignable:
2337+
DiagnoseNonAssignableReason(*this, E->getBeginLoc(), Args[0], Args[1]);
2338+
break;
23082339
default:
23092340
break;
23102341
}

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ struct is_trivially_copyable {
2020

2121
template <typename T>
2222
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
23+
24+
template <typename T, typename U>
25+
struct is_assignable {
26+
static constexpr bool value = __is_assignable(T, U);
27+
};
28+
29+
template <typename T, typename U>
30+
constexpr bool is_assignable_v = __is_assignable(T, U);
2331
#endif
2432

2533
#ifdef STD2
@@ -44,6 +52,17 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>;
4452

4553
template <typename T>
4654
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
55+
56+
template <typename T, typename U>
57+
struct __details_is_assignable {
58+
static constexpr bool value = __is_assignable(T, U);
59+
};
60+
61+
template <typename T, typename U>
62+
using is_assignable = __details_is_assignable<T, U>;
63+
64+
template <typename T, typename U>
65+
constexpr bool is_assignable_v = __is_assignable(T, U);
4766
#endif
4867

4968

@@ -73,6 +92,15 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>;
7392

7493
template <typename T>
7594
constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
95+
96+
template <typename T, typename U>
97+
struct __details_is_assignable : bool_constant<__is_assignable(T, U)> {};
98+
99+
template <typename T, typename U>
100+
using is_assignable = __details_is_assignable<T, U>;
101+
102+
template <typename T, typename U>
103+
constexpr bool is_assignable_v = is_assignable<T, U>::value;
76104
#endif
77105

78106
}
@@ -99,6 +127,14 @@ static_assert(std::is_trivially_copyable_v<int&>);
99127
// expected-note@-1 {{'int &' is not trivially copyable}} \
100128
// expected-note@-1 {{because it is a reference type}}
101129

130+
static_assert(std::is_assignable<int&, int>::value);
131+
132+
static_assert(std::is_assignable<int&, void>::value);
133+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_assignable<int &, void>::value'}} \
134+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
135+
static_assert(std::is_assignable_v<int&, void>);
136+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_assignable_v<int &, void>'}} \
137+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
102138

103139
namespace test_namespace {
104140
using namespace std;
@@ -119,6 +155,13 @@ namespace test_namespace {
119155
// expected-error@-1 {{static assertion failed due to requirement 'is_trivially_copyable_v<int &>'}} \
120156
// expected-note@-1 {{'int &' is not trivially copyable}} \
121157
// expected-note@-1 {{because it is a reference type}}
158+
159+
static_assert(is_assignable<int&, void>::value);
160+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_assignable<int &, void>::value'}} \
161+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
162+
static_assert(is_assignable_v<int&, void>);
163+
// expected-error@-1 {{static assertion failed due to requirement 'is_assignable_v<int &, void>'}} \
164+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
122165
}
123166

124167

@@ -139,6 +182,14 @@ concept C2 = std::is_trivially_copyable_v<T>; // #concept4
139182

140183
template <C2 T> void g2(); // #cand4
141184

185+
template <typename T, typename U>
186+
requires std::is_assignable<T, U>::value void f4(); // #cand7
187+
188+
template <typename T, typename U>
189+
concept C4 = std::is_assignable_v<T, U>; // #concept8
190+
191+
template <C4<void> T> void g4(); // #cand8
192+
142193
void test() {
143194
f<int&>();
144195
// expected-error@-1 {{no matching function for call to 'f'}} \
@@ -169,6 +220,19 @@ void test() {
169220
// expected-note@#concept4 {{because 'std::is_trivially_copyable_v<int &>' evaluated to false}} \
170221
// expected-note@#concept4 {{'int &' is not trivially copyable}} \
171222
// expected-note@#concept4 {{because it is a reference type}}
223+
224+
f4<int&, void>();
225+
// expected-error@-1 {{no matching function for call to 'f4'}} \
226+
// expected-note@#cand7 {{candidate template ignored: constraints not satisfied [with T = int &, U = void]}} \
227+
// expected-note-re@#cand7 {{because '{{.*}}is_assignable<int &, void>::value' evaluated to false}} \
228+
// expected-error@#cand7 {{assigning to 'int' from incompatible type 'void'}}
229+
230+
g4<int&>();
231+
// expected-error@-1 {{no matching function for call to 'g4'}} \
232+
// expected-note@#cand8 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
233+
// expected-note@#cand8 {{because 'C4<int &, void>' evaluated to false}} \
234+
// expected-note@#concept8 {{because 'std::is_assignable_v<int &, void>' evaluated to false}} \
235+
// expected-error@#concept8 {{assigning to 'int' from incompatible type 'void'}}
172236
}
173237
}
174238

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,3 +488,74 @@ static_assert(__is_trivially_copyable(S12));
488488
// expected-note@-1 {{'S12' is not trivially copyable}} \
489489
// expected-note@#tc-S12 {{'S12' defined here}}
490490
}
491+
492+
namespace assignable {
493+
struct S1;
494+
static_assert(__is_assignable(S1&, const S1&));
495+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::S1 &, const assignable::S1 &)'}} \
496+
// expected-error@-1 {{no viable overloaded '='}} \
497+
// expected-note@-1 {{type 'S1' is incomplete}}
498+
499+
static_assert(__is_assignable(void, int));
500+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(void, int)'}} \
501+
// expected-error@-1 {{expression is not assignable}}
502+
503+
static_assert(__is_assignable(int, int));
504+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int, int)'}} \
505+
// expected-error@-1 {{expression is not assignable}}
506+
507+
static_assert(__is_assignable(int*, int));
508+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int *, int)'}} \
509+
// expected-error@-1 {{expression is not assignable}}
510+
511+
static_assert(__is_assignable(int[], int));
512+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int[], int)'}} \
513+
// expected-error@-1 {{expression is not assignable}}
514+
515+
static_assert(__is_assignable(int&, void));
516+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int &, void)'}} \
517+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
518+
519+
static_assert(__is_assignable(int*&, float*));
520+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int *&, float *)'}} \
521+
// expected-error@-1 {{incompatible pointer types assigning to 'int *' from 'float *'}}
522+
523+
static_assert(__is_assignable(const int&, int));
524+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(const int &, int)'}} \
525+
// expected-error@-1 {{read-only variable is not assignable}}
526+
527+
struct S2 {}; // #a-S2
528+
static_assert(__is_assignable(const S2, S2));
529+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(const assignable::S2, assignable::S2)'}} \
530+
// expected-error@-1 {{no viable overloaded '='}} \
531+
// expected-note@#a-S2 {{candidate function (the implicit copy assignment operator) not viable: 'this' argument has type 'const S2', but method is not marked const}} \
532+
// expected-note@#a-S2 {{candidate function (the implicit move assignment operator) not viable: 'this' argument has type 'const S2', but method is not marked const}} \
533+
// expected-note@#a-S2 {{'S2' defined here}}
534+
535+
struct S3 { // #a-S3
536+
S3& operator=(const S3&) = delete; // #aca-S3
537+
S3& operator=(S3&&) = delete; // #ama-S3
538+
};
539+
static_assert(__is_assignable(S3, const S3&));
540+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::S3, const assignable::S3 &)'}} \
541+
// expected-error@-1 {{overload resolution selected deleted operator '='}} \
542+
// expected-note@#aca-S3 {{candidate function has been explicitly deleted}} \
543+
// expected-note@#ama-S3 {{candidate function not viable: 1st argument ('const S3') would lose const qualifier}} \
544+
// expected-note@#a-S3 {{'S3' defined here}}
545+
static_assert(__is_assignable(S3, S3&&));
546+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::S3, assignable::S3 &&)'}} \
547+
// expected-error@-1 {{overload resolution selected deleted operator '='}} \
548+
// expected-note@#aca-S3 {{candidate function has been explicitly deleted}} \
549+
// expected-note@#ama-S3 {{candidate function has been explicitly deleted}} \
550+
// expected-note@#a-S3 {{'S3' defined here}}
551+
552+
class C1 { // #a-C1
553+
C1& operator=(const C1&) = default;
554+
C1& operator=(C1&&) = default; // #ama-C1
555+
};
556+
static_assert(__is_assignable(C1, C1));
557+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::C1, assignable::C1)'}} \
558+
// expected-error@-1 {{'operator=' is a private member of 'assignable::C1'}} \
559+
// expected-note@#ama-C1 {{implicitly declared private here}} \
560+
// expected-note@#a-C1 {{'C1' defined here}}
561+
}

0 commit comments

Comments
 (0)