-
Notifications
You must be signed in to change notification settings - Fork 14.1k
[Clang] Diagnose unsatisfied std::is_assignable
.
#144836
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
Open
rkirsling
wants to merge
1
commit into
llvm:main
Choose a base branch
from
rkirsling:diagnose-unsatisfied-is_assignable
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
+164
−0
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@llvm/pr-subscribers-clang Author: Ross Kirsling (rkirsling) ChangesPart of the work for #141911. Checking Full diff: https://github.com/llvm/llvm-project/pull/144836.diff 3 Files Affected:
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 22c690bedc1ed..aa3208fe51271 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1949,6 +1949,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
.Case("is_constructible", TypeTrait::TT_IsConstructible)
+ .Case("is_assignable", TypeTrait::BTT_IsAssignable)
.Default(std::nullopt);
}
@@ -2340,6 +2341,31 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}
+static void DiagnoseNonAssignableReason(Sema &SemaRef, SourceLocation Loc,
+ QualType T, QualType U) {
+ const CXXRecordDecl *D = T->getAsCXXRecordDecl();
+
+ if (T->isObjectType() || T->isFunctionType())
+ T = SemaRef.Context.getRValueReferenceType(T);
+ if (U->isObjectType() || U->isFunctionType())
+ U = SemaRef.Context.getRValueReferenceType(U);
+ OpaqueValueExpr LHS(Loc, T.getNonLValueExprType(SemaRef.Context),
+ Expr::getValueKindForType(T));
+ OpaqueValueExpr RHS(Loc, U.getNonLValueExprType(SemaRef.Context),
+ Expr::getValueKindForType(U));
+
+ EnterExpressionEvaluationContext Unevaluated(
+ SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
+ Sema::ContextRAII TUContext(SemaRef,
+ SemaRef.Context.getTranslationUnitDecl());
+ SemaRef.BuildBinOp(/*S=*/nullptr, Loc, BO_Assign, &LHS, &RHS);
+
+ if (!D || D->isInvalidDecl())
+ return;
+
+ SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
+}
+
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
E = E->IgnoreParenImpCasts();
if (E->containsErrors())
@@ -2363,6 +2389,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
case TT_IsConstructible:
DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
break;
+ case BTT_IsAssignable:
+ DiagnoseNonAssignableReason(*this, E->getBeginLoc(), Args[0], Args[1]);
+ break;
default:
break;
}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
index a403a0450607a..23391a799282f 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -28,6 +28,14 @@ struct is_constructible {
template <typename... Args>
constexpr bool is_constructible_v = __is_constructible(Args...);
+
+template <typename T, typename U>
+struct is_assignable {
+ static constexpr bool value = __is_assignable(T, U);
+};
+
+template <typename T, typename U>
+constexpr bool is_assignable_v = __is_assignable(T, U);
#endif
#ifdef STD2
@@ -63,6 +71,17 @@ using is_constructible = __details_is_constructible<Args...>;
template <typename... Args>
constexpr bool is_constructible_v = __is_constructible(Args...);
+
+template <typename T, typename U>
+struct __details_is_assignable {
+ static constexpr bool value = __is_assignable(T, U);
+};
+
+template <typename T, typename U>
+using is_assignable = __details_is_assignable<T, U>;
+
+template <typename T, typename U>
+constexpr bool is_assignable_v = __is_assignable(T, U);
#endif
@@ -101,6 +120,15 @@ using is_constructible = __details_is_constructible<Args...>;
template <typename... Args>
constexpr bool is_constructible_v = is_constructible<Args...>::value;
+
+template <typename T, typename U>
+struct __details_is_assignable : bool_constant<__is_assignable(T, U)> {};
+
+template <typename T, typename U>
+using is_assignable = __details_is_assignable<T, U>;
+
+template <typename T, typename U>
+constexpr bool is_assignable_v = is_assignable<T, U>::value;
#endif
}
@@ -137,6 +165,15 @@ static_assert(std::is_constructible_v<void>);
// expected-error@-1 {{static assertion failed due to requirement 'std::is_constructible_v<void>'}} \
// expected-note@-1 {{because it is a cv void type}}
+static_assert(std::is_assignable<int&, int>::value);
+
+static_assert(std::is_assignable<int&, void>::value);
+// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_assignable<int &, void>::value'}} \
+// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
+static_assert(std::is_assignable_v<int&, void>);
+// expected-error@-1 {{static assertion failed due to requirement 'std::is_assignable_v<int &, void>'}} \
+// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
+
namespace test_namespace {
using namespace std;
static_assert(is_trivially_relocatable<int&>::value);
@@ -163,6 +200,13 @@ namespace test_namespace {
static_assert(is_constructible_v<void>);
// expected-error@-1 {{static assertion failed due to requirement 'is_constructible_v<void>'}} \
// expected-note@-1 {{because it is a cv void type}}
+
+ static_assert(is_assignable<int&, void>::value);
+ // expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_assignable<int &, void>::value'}} \
+ // expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
+ static_assert(is_assignable_v<int&, void>);
+ // expected-error@-1 {{static assertion failed due to requirement 'is_assignable_v<int &, void>'}} \
+ // expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
}
@@ -191,6 +235,14 @@ concept C3 = std::is_constructible_v<Args...>; // #concept6
template <C3 T> void g3(); // #cand6
+template <typename T, typename U>
+requires std::is_assignable<T, U>::value void f4(); // #cand7
+
+template <typename T, typename U>
+concept C4 = std::is_assignable_v<T, U>; // #concept8
+
+template <C4<void> T> void g4(); // #cand8
+
void test() {
f<int&>();
@@ -235,6 +287,19 @@ void test() {
// expected-note@#cand6 {{because 'void' does not satisfy 'C3'}} \
// expected-note@#concept6 {{because 'std::is_constructible_v<void>' evaluated to false}} \
// expected-note@#concept6 {{because it is a cv void type}}
+
+ f4<int&, void>();
+ // expected-error@-1 {{no matching function for call to 'f4'}} \
+ // expected-note@#cand7 {{candidate template ignored: constraints not satisfied [with T = int &, U = void]}} \
+ // expected-note-re@#cand7 {{because '{{.*}}is_assignable<int &, void>::value' evaluated to false}} \
+ // expected-error@#cand7 {{assigning to 'int' from incompatible type 'void'}}
+
+ g4<int&>();
+ // expected-error@-1 {{no matching function for call to 'g4'}} \
+ // expected-note@#cand8 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
+ // expected-note@#cand8 {{because 'C4<int &, void>' evaluated to false}} \
+ // expected-note@#concept8 {{because 'std::is_assignable_v<int &, void>' evaluated to false}} \
+ // expected-error@#concept8 {{assigning to 'int' from incompatible type 'void'}}
}
}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index d0b3f294fbcab..adca5938c3759 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -550,3 +550,74 @@ static_assert(__is_constructible(void (int, float)));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(void (int, float))'}} \
// expected-note@-1 {{because it is a function type}}
}
+
+namespace assignable {
+struct S1;
+static_assert(__is_assignable(S1&, const S1&));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::S1 &, const assignable::S1 &)'}} \
+// expected-error@-1 {{no viable overloaded '='}} \
+// expected-note@-1 {{type 'S1' is incomplete}}
+
+static_assert(__is_assignable(void, int));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(void, int)'}} \
+// expected-error@-1 {{expression is not assignable}}
+
+static_assert(__is_assignable(int, int));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int, int)'}} \
+// expected-error@-1 {{expression is not assignable}}
+
+static_assert(__is_assignable(int*, int));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int *, int)'}} \
+// expected-error@-1 {{expression is not assignable}}
+
+static_assert(__is_assignable(int[], int));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int[], int)'}} \
+// expected-error@-1 {{expression is not assignable}}
+
+static_assert(__is_assignable(int&, void));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int &, void)'}} \
+// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
+
+static_assert(__is_assignable(int*&, float*));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(int *&, float *)'}} \
+// expected-error@-1 {{incompatible pointer types assigning to 'int *' from 'float *'}}
+
+static_assert(__is_assignable(const int&, int));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(const int &, int)'}} \
+// expected-error@-1 {{read-only variable is not assignable}}
+
+struct S2 {}; // #a-S2
+static_assert(__is_assignable(const S2, S2));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(const assignable::S2, assignable::S2)'}} \
+// expected-error@-1 {{no viable overloaded '='}} \
+// 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}} \
+// 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}} \
+// expected-note@#a-S2 {{'S2' defined here}}
+
+struct S3 { // #a-S3
+ S3& operator=(const S3&) = delete; // #aca-S3
+ S3& operator=(S3&&) = delete; // #ama-S3
+};
+static_assert(__is_assignable(S3, const S3&));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::S3, const assignable::S3 &)'}} \
+// expected-error@-1 {{overload resolution selected deleted operator '='}} \
+// expected-note@#aca-S3 {{candidate function has been explicitly deleted}} \
+// expected-note@#ama-S3 {{candidate function not viable: 1st argument ('const S3') would lose const qualifier}} \
+// expected-note@#a-S3 {{'S3' defined here}}
+static_assert(__is_assignable(S3, S3&&));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::S3, assignable::S3 &&)'}} \
+// expected-error@-1 {{overload resolution selected deleted operator '='}} \
+// expected-note@#aca-S3 {{candidate function has been explicitly deleted}} \
+// expected-note@#ama-S3 {{candidate function has been explicitly deleted}} \
+// expected-note@#a-S3 {{'S3' defined here}}
+
+class C1 { // #a-C1
+ C1& operator=(const C1&) = default;
+ C1& operator=(C1&&) = default; // #ama-C1
+};
+static_assert(__is_assignable(C1, C1));
+// expected-error@-1 {{static assertion failed due to requirement '__is_assignable(assignable::C1, assignable::C1)'}} \
+// expected-error@-1 {{'operator=' is a private member of 'assignable::C1'}} \
+// expected-note@#ama-C1 {{implicitly declared private here}} \
+// expected-note@#a-C1 {{'C1' defined here}}
+}
|
Part of the work for llvm#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.
ac74e32
to
5807503
Compare
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"
clang
Clang issues not falling into any other category
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Part of the work for #141911.
Checking
is_assignable<T, U>
boils down to checking the well-formedness ofdeclval<T>() = declval<U>()
; this PR recycles logic from EvaluateBinaryTypeTrait in order to produce the relevant diagnostics.