Skip to content

Commit ac74e32

Browse files
committed
[Clang] Diagnose unsatisfied std::is_assignable.
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 9a237f3 commit ac74e32

File tree

3 files changed

+165
-0
lines changed

3 files changed

+165
-0
lines changed

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,6 +1949,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
19491949
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
19501950
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
19511951
.Case("is_constructible", TypeTrait::TT_IsConstructible)
1952+
.Case("is_assignable", TypeTrait::BTT_IsAssignable)
19521953
.Default(std::nullopt);
19531954
}
19541955

@@ -2340,6 +2341,31 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
23402341
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
23412342
}
23422343

2344+
static void DiagnoseNonAssignableReason(Sema &SemaRef, SourceLocation Loc,
2345+
QualType T, QualType U) {
2346+
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
2347+
2348+
if (T->isObjectType() || T->isFunctionType())
2349+
T = SemaRef.Context.getRValueReferenceType(T);
2350+
if (U->isObjectType() || U->isFunctionType())
2351+
U = SemaRef.Context.getRValueReferenceType(U);
2352+
OpaqueValueExpr LHS(Loc, T.getNonLValueExprType(SemaRef.Context),
2353+
Expr::getValueKindForType(T));
2354+
OpaqueValueExpr RHS(Loc, U.getNonLValueExprType(SemaRef.Context),
2355+
Expr::getValueKindForType(U));
2356+
2357+
EnterExpressionEvaluationContext Unevaluated(
2358+
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
2359+
Sema::ContextRAII TUContext(SemaRef,
2360+
SemaRef.Context.getTranslationUnitDecl());
2361+
SemaRef.BuildBinOp(/*S=*/nullptr, Loc, BO_Assign, &LHS, &RHS);
2362+
2363+
if (!D || D->isInvalidDecl())
2364+
return;
2365+
2366+
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
2367+
}
2368+
23432369
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
23442370
E = E->IgnoreParenImpCasts();
23452371
if (E->containsErrors())
@@ -2363,6 +2389,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
23632389
case TT_IsConstructible:
23642390
DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
23652391
break;
2392+
case BTT_IsAssignable:
2393+
DiagnoseNonAssignableReason(*this, E->getBeginLoc(), Args[0], Args[1]);
2394+
break;
23662395
default:
23672396
break;
23682397
}

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ struct is_constructible {
2828

2929
template <typename... Args>
3030
constexpr bool is_constructible_v = __is_constructible(Args...);
31+
32+
template <typename T, typename U>
33+
struct is_assignable {
34+
static constexpr bool value = __is_assignable(T, U);
35+
};
36+
37+
template <typename T, typename U>
38+
constexpr bool is_assignable_v = __is_assignable(T, U);
3139
#endif
3240

3341
#ifdef STD2
@@ -63,6 +71,17 @@ using is_constructible = __details_is_constructible<Args...>;
6371

6472
template <typename... Args>
6573
constexpr bool is_constructible_v = __is_constructible(Args...);
74+
75+
template <typename T, typename U>
76+
struct __details_is_assignable {
77+
static constexpr bool value = __is_assignable(T, U);
78+
};
79+
80+
template <typename T, typename U>
81+
using is_assignable = __details_is_assignable<T, U>;
82+
83+
template <typename T, typename U>
84+
constexpr bool is_assignable_v = __is_assignable(T, U);
6685
#endif
6786

6887

@@ -101,6 +120,15 @@ using is_constructible = __details_is_constructible<Args...>;
101120

102121
template <typename... Args>
103122
constexpr bool is_constructible_v = is_constructible<Args...>::value;
123+
124+
template <typename T, typename U>
125+
struct __details_is_assignable : bool_constant<__is_assignable(T, U)> {};
126+
127+
template <typename T, typename U>
128+
using is_assignable = __details_is_assignable<T, U>;
129+
130+
template <typename T, typename U>
131+
constexpr bool is_assignable_v = is_assignable<T, U>::value;
104132
#endif
105133

106134
}
@@ -137,6 +165,15 @@ static_assert(std::is_constructible_v<void>);
137165
// expected-error@-1 {{static assertion failed due to requirement 'std::is_constructible_v<void>'}} \
138166
// expected-note@-1 {{because it is a cv void type}}
139167

168+
static_assert(std::is_assignable<int&, int>::value);
169+
170+
static_assert(std::is_assignable<int&, void>::value);
171+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_assignable<int &, void>::value'}} \
172+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
173+
static_assert(std::is_assignable_v<int&, void>);
174+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_assignable_v<int &, void>'}} \
175+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
176+
140177
namespace test_namespace {
141178
using namespace std;
142179
static_assert(is_trivially_relocatable<int&>::value);
@@ -163,6 +200,13 @@ namespace test_namespace {
163200
static_assert(is_constructible_v<void>);
164201
// expected-error@-1 {{static assertion failed due to requirement 'is_constructible_v<void>'}} \
165202
// expected-note@-1 {{because it is a cv void type}}
203+
204+
static_assert(is_assignable<int&, void>::value);
205+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_assignable<int &, void>::value'}} \
206+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
207+
static_assert(is_assignable_v<int&, void>);
208+
// expected-error@-1 {{static assertion failed due to requirement 'is_assignable_v<int &, void>'}} \
209+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
166210
}
167211

168212

@@ -191,6 +235,14 @@ concept C3 = std::is_constructible_v<Args...>; // #concept6
191235

192236
template <C3 T> void g3(); // #cand6
193237

238+
template <typename T, typename U>
239+
requires std::is_assignable<T, U>::value void f4(); // #cand7
240+
241+
template <typename T, typename U>
242+
concept C4 = std::is_assignable_v<T, U>; // #concept8
243+
244+
template <C4<void> T> void g4(); // #cand8
245+
194246

195247
void test() {
196248
f<int&>();
@@ -235,6 +287,19 @@ void test() {
235287
// expected-note@#cand6 {{because 'void' does not satisfy 'C3'}} \
236288
// expected-note@#concept6 {{because 'std::is_constructible_v<void>' evaluated to false}} \
237289
// expected-note@#concept6 {{because it is a cv void type}}
290+
291+
f4<int&, void>();
292+
// expected-error@-1 {{no matching function for call to 'f4'}} \
293+
// expected-note@#cand7 {{candidate template ignored: constraints not satisfied [with T = int &, U = void]}} \
294+
// expected-note-re@#cand7 {{because '{{.*}}is_assignable<int &, void>::value' evaluated to false}} \
295+
// expected-error@#cand7 {{assigning to 'int' from incompatible type 'void'}}
296+
297+
g4<int&>();
298+
// expected-error@-1 {{no matching function for call to 'g4'}} \
299+
// expected-note@#cand8 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
300+
// expected-note@#cand8 {{because 'C4<int &, void>' evaluated to false}} \
301+
// expected-note@#concept8 {{because 'std::is_assignable_v<int &, void>' evaluated to false}} \
302+
// expected-error@#concept8 {{assigning to 'int' from incompatible type 'void'}}
238303
}
239304
}
240305

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,3 +550,74 @@ static_assert(__is_constructible(void (int, float)));
550550
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(void (int, float))'}} \
551551
// expected-note@-1 {{because it is a function type}}
552552
}
553+
554+
namespace assignable {
555+
struct S1;
556+
static_assert(__is_assignable(S1&, const S1&));
557+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::S1 &, const assignable::S1 &)'}} \
558+
// expected-error@-1 {{no viable overloaded '='}} \
559+
// expected-note@-1 {{type 'S1' is incomplete}}
560+
561+
static_assert(__is_assignable(void, int));
562+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(void, int)'}} \
563+
// expected-error@-1 {{expression is not assignable}}
564+
565+
static_assert(__is_assignable(int, int));
566+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int, int)'}} \
567+
// expected-error@-1 {{expression is not assignable}}
568+
569+
static_assert(__is_assignable(int*, int));
570+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int *, int)'}} \
571+
// expected-error@-1 {{expression is not assignable}}
572+
573+
static_assert(__is_assignable(int[], int));
574+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int[], int)'}} \
575+
// expected-error@-1 {{expression is not assignable}}
576+
577+
static_assert(__is_assignable(int&, void));
578+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int &, void)'}} \
579+
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
580+
581+
static_assert(__is_assignable(int*&, float*));
582+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int *&, float *)'}} \
583+
// expected-error@-1 {{incompatible pointer types assigning to 'int *' from 'float *'}}
584+
585+
static_assert(__is_assignable(const int&, int));
586+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(const int &, int)'}} \
587+
// expected-error@-1 {{read-only variable is not assignable}}
588+
589+
struct S2 {}; // #a-S2
590+
static_assert(__is_assignable(const S2, S2));
591+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(const assignable::S2, assignable::S2)'}} \
592+
// expected-error@-1 {{no viable overloaded '='}} \
593+
// 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}} \
594+
// 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}} \
595+
// expected-note@#a-S2 {{'S2' defined here}}
596+
597+
struct S3 { // #a-S3
598+
S3& operator=(const S3&) = delete; // #aca-S3
599+
S3& operator=(S3&&) = delete; // #ama-S3
600+
};
601+
static_assert(__is_assignable(S3, const S3&));
602+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::S3, const assignable::S3 &)'}} \
603+
// expected-error@-1 {{overload resolution selected deleted operator '='}} \
604+
// expected-note@#aca-S3 {{candidate function has been explicitly deleted}} \
605+
// expected-note@#ama-S3 {{candidate function not viable: 1st argument ('const S3') would lose const qualifier}} \
606+
// expected-note@#a-S3 {{'S3' defined here}}
607+
static_assert(__is_assignable(S3, S3&&));
608+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::S3, assignable::S3 &&)'}} \
609+
// expected-error@-1 {{overload resolution selected deleted operator '='}} \
610+
// expected-note@#aca-S3 {{candidate function has been explicitly deleted}} \
611+
// expected-note@#ama-S3 {{candidate function has been explicitly deleted}} \
612+
// expected-note@#a-S3 {{'S3' defined here}}
613+
614+
class C1 { // #a-C1
615+
C1& operator=(const C1&) = default;
616+
C1& operator=(C1&&) = default; // #ama-C1
617+
};
618+
static_assert(__is_assignable(C1, C1));
619+
// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::C1, assignable::C1)'}} \
620+
// expected-error@-1 {{'operator=' is a private member of 'assignable::C1'}} \
621+
// expected-note@#ama-C1 {{implicitly declared private here}} \
622+
// expected-note@#a-C1 {{'C1' defined here}}
623+
}

0 commit comments

Comments
 (0)