-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[Clang] Add diagnostic reasoning for unsatisfied is_destructible trait #167291
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
base: main
Are you sure you want to change the base?
Conversation
|
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
|
@llvm/pr-subscribers-clang Author: None (AnushaK6) ChangesThis patch adds diagnostic reasoning for the std::is_destructible type trait in Clang’s Sema diagnostics, in response to issue #141911 Changes:
Added new tests to:
Full diff: https://github.com/llvm/llvm-project/pull/167291.diff 4 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3e864475f22a1..8fb76c2c989f5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1776,7 +1776,8 @@ def note_unsatisfied_trait
"%StandardLayout{standard-layout}|"
"%Aggregate{aggregate}|"
"%Final{final}|"
- "%Abstract{abstract}"
+ "%Abstract{abstract}|"
+ "%Destructible{destructible}"
"}1">;
def note_unsatisfied_trait_reason
@@ -1808,6 +1809,7 @@ def note_unsatisfied_trait_reason
"%NonStandardLayoutMember{has a non-standard-layout member %1 of type %2}|"
"%IndirectBaseWithFields{has an indirect base %1 with data members}|"
"%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
+ "%InaccessibleDtr{has a %select{private|protected}1 destructor}|"
"%UserProvidedCtr{has a user provided %select{copy|move}1 "
"constructor}|"
"%UserDeclaredCtr{has a user-declared constructor}|"
@@ -1823,6 +1825,7 @@ def note_unsatisfied_trait_reason
"%FunctionType{is a function type}|"
"%CVVoidType{is a cv void type}|"
"%IncompleteArrayType{is an incomplete array type}|"
+ "%IncompleteType{is an incomplete type}|"
"%PrivateProtectedDirectDataMember{has a %select{private|protected}1 direct data member}|"
"%PrivateProtectedDirectBase{has a %select{private|protected}1 direct base}|"
"%NotClassOrUnion{is not a class or union type}|"
@@ -12137,9 +12140,6 @@ def err_omp_unexpected_schedule_modifier : Error<
"modifier '%0' cannot be used along with modifier '%1'">;
def err_omp_schedule_nonmonotonic_static : Error<
"'nonmonotonic' modifier can only be specified with 'dynamic' or 'guided' schedule kind">;
-def err_omp_incompatible_dyn_groupprivate_modifier
- : Error<"modifier '%0' cannot be used along with modifier '%1' in "
- "dyn_groupprivate">;
def err_omp_simple_clause_incompatible_with_ordered : Error<
"'%0' clause with '%1' modifier cannot be specified if an 'ordered' clause is specified">;
def err_omp_ordered_simd : Error<
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 38877967af05e..fef47dfc2cc51 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -2028,6 +2028,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
.Case("is_constructible", TypeTrait::TT_IsConstructible)
.Case("is_final", TypeTrait::UTT_IsFinal)
.Case("is_abstract", TypeTrait::UTT_IsAbstract)
+ .Case("is_destructible", TypeTrait::UTT_IsDestructible)
.Default(std::nullopt);
}
@@ -2399,6 +2400,66 @@ static void DiagnoseNonConstructibleReason(
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}
+static void DiagnoseNonDestructibleReason(Sema &SemaRef, SourceLocation Loc,
+ QualType T) {
+
+ QualType CoreT = T.getCanonicalType();
+ if (const ArrayType *AT = SemaRef.Context.getAsArrayType(CoreT))
+ CoreT = AT->getElementType();
+
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
+ << CoreT << diag::TraitName::Destructible;
+
+ if (CoreT->isFunctionType()) {
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::FunctionType;
+ return;
+ }
+
+ if (CoreT->isVoidType()) {
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::CVVoidType;
+ return;
+ }
+
+ if (CoreT->isIncompleteType()) {
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::IncompleteType;
+ return;
+ }
+
+ const CXXRecordDecl *RD = CoreT->getAsCXXRecordDecl();
+ if (!RD || RD->isInvalidDecl())
+ return;
+
+ const CXXRecordDecl *Def = RD->getDefinition();
+ if (!Def) {
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::IncompleteType;
+ return;
+ }
+
+ CXXDestructorDecl *Dtor = Def->getDestructor();
+ if (!Dtor)
+ return;
+
+ if (Dtor->isDeleted()) {
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::DeletedDtr << 0
+ << Dtor->getSourceRange();
+ return;
+ }
+
+ AccessSpecifier AS = Dtor->getAccess();
+ if (AS == AS_private || AS == AS_protected) {
+ unsigned Select = (AS == AS_private) ? 0 : 1;
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::InaccessibleDtr << Select
+ << Dtor->getSourceRange();
+ return;
+ }
+}
+
static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
SourceLocation Loc, QualType T) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
@@ -2889,6 +2950,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
case TT_IsConstructible:
DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
break;
+ case UTT_IsDestructible:
+ DiagnoseNonDestructibleReason(*this, E->getBeginLoc(), Args[0]);
+ break;
case UTT_IsAggregate:
DiagnoseNonAggregateReason(*this, E->getBeginLoc(), Args[0]);
break;
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
index 3e03a79275232..3e02fe8f10f56 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -73,6 +73,15 @@ struct is_abstract {
template <typename T>
constexpr bool is_abstract_v = __is_abstract(T);
+template <typename T>
+struct is_destructible {
+ static constexpr bool value = __is_destructible(T);
+};
+
+template <typename T>
+constexpr bool is_destructible_v = __is_destructible(T);
+
+
#endif
#ifdef STD2
@@ -167,6 +176,17 @@ using is_abstract = __details_is_abstract<T>;
template <typename T>
constexpr bool is_abstract_v = __is_abstract(T);
+template <typename T>
+struct __details_is_destructible {
+ static constexpr bool value = __is_destructible(T);
+};
+
+template <typename T>
+using is_destructible = __details_is_destructible<T>;
+
+template <typename T>
+constexpr bool is_destructible_v = __is_destructible(T);
+
#endif
@@ -252,6 +272,15 @@ using is_abstract = __details_is_abstract<T>;
template <typename T>
constexpr bool is_abstract_v = is_abstract<T>::value;
+template <typename T>
+struct __details_is_destructible : bool_constant<__is_destructible(T)> {};
+
+template <typename T>
+using is_destructible = __details_is_destructible<T>;
+
+template <typename T>
+constexpr bool is_destructible_v = is_destructible<T>::value;
+
#endif
}
@@ -374,6 +403,18 @@ static_assert(std::is_abstract_v<int&>);
// expected-note@-1 {{because it is a reference type}} \
// expected-note@-1 {{because it is not a struct or class type}}
+static_assert(std::is_destructible<int>::value);
+
+static_assert(std::is_destructible<void>::value);
+// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_destructible<void>::value'}} \
+// expected-note@-1 {{'void' is not destructible}} \
+// expected-note@-1 {{because it is a cv void type}}
+
+static_assert(std::is_destructible_v<void>);
+// expected-error@-1 {{static assertion failed due to requirement 'std::is_destructible_v<void>'}} \
+// expected-note@-1 {{'void' is not destructible}} \
+// expected-note@-1 {{because it is a cv void type}}
+
namespace test_namespace {
using namespace std;
@@ -473,6 +514,17 @@ namespace test_namespace {
// expected-note@-1 {{'int &' is not abstract}} \
// expected-note@-1 {{because it is a reference type}} \
// expected-note@-1 {{because it is not a struct or class type}}
+
+ static_assert(is_destructible<void>::value);
+ // expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_destructible<void>::value'}} \
+ // expected-note@-1 {{'void' is not destructible}} \
+ // expected-note@-1 {{because it is a cv void type}}
+
+ static_assert(is_destructible_v<void>);
+ // expected-error@-1 {{static assertion failed due to requirement 'is_destructible_v<void>'}} \
+ // expected-note@-1 {{'void' is not destructible}} \
+ // expected-note@-1 {{because it is a cv void type}}
+
}
@@ -518,6 +570,15 @@ concept C5 = std::is_aggregate_v<T>; // #concept10
template <C5 T> void g5(); // #cand10
+template <typename T>
+requires std::is_destructible<T>::value void f6(); // #cand11
+
+template <typename T>
+concept C6 = std::is_destructible_v<T>; // #concept11
+
+template <C6 T> void g6(); // #cand12
+
+
void test() {
f<int&>();
// expected-error@-1 {{no matching function for call to 'f'}} \
@@ -589,6 +650,21 @@ void test() {
// expected-note@#concept10 {{because 'std::is_aggregate_v<void>' evaluated to false}} \
// expected-note@#concept10 {{'void' is not aggregate}} \
// expected-note@#concept10 {{because it is a cv void type}}
+
+ f6<void>();
+ // expected-error@-1 {{no matching function for call to 'f6'}} \
+ // expected-note@#cand11 {{candidate template ignored: constraints not satisfied [with T = void]}} \
+ // expected-note-re@#cand11 {{because '{{.*}}is_destructible<void>::value' evaluated to false}} \
+ // expected-note@#cand11 {{'void' is not destructible}} \
+ // expected-note@#cand11 {{because it is a cv void type}}
+
+ g6<void>();
+ // expected-error@-1 {{no matching function for call to 'g6'}} \
+ // expected-note@#cand12 {{candidate template ignored: constraints not satisfied [with T = void]}} \
+ // expected-note@#cand12 {{because 'void' does not satisfy 'C6'}} \
+ // expected-note@#concept11 {{because 'std::is_destructible_v<void>' evaluated to false}} \
+ // expected-note@#concept11 {{'void' is not destructible}} \
+ // expected-note@#concept11 {{because it is a cv void type}}
}
}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index 22740418f09f5..858a5cc24868f 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -1052,3 +1052,52 @@ static_assert(__is_abstract(U));
// expected-note@-1 {{because it is not a struct or class type}}
}
+namespace destructible {
+
+struct Incomplete; // expected-note {{forward declaration of 'destructible::Incomplete'}}
+static_assert(__is_destructible(Incomplete));
+// expected-error@-1 {{incomplete type 'Incomplete' used in type trait expression}}
+
+static_assert(__is_destructible(void));
+// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(void)'}} \
+// expected-note@-1 {{'void' is not destructible}} \
+// expected-note@-1 {{because it is a cv void type}}
+
+using F = void();
+static_assert(__is_destructible(F));
+// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(void ())'}} \
+// expected-note@-1 {{'void ()' is not destructible}} \
+// expected-note@-1 {{because it is a function type}}
+
+using Ref = int&;
+static_assert(__is_destructible(Ref)); // no diagnostics (true)
+
+struct DeletedDtor { // #d-DeletedDtor
+ ~DeletedDtor() = delete;
+};
+static_assert(__is_destructible(DeletedDtor));
+// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(destructible::DeletedDtor)'}} \
+// expected-note@-1 {{'destructible::DeletedDtor' is not destructible}} \
+// expected-note@-1 {{because it has a deleted destructor}}
+
+struct PrivateDtor { // #d-PrivateDtor
+private:
+ ~PrivateDtor(); // #d-PrivateDtor-dtor
+};
+static_assert(__is_destructible(PrivateDtor));
+// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(destructible::PrivateDtor)'}} \
+// expected-note@-1 {{'destructible::PrivateDtor' is not destructible}} \
+// expected-note@-1 {{because it has a private destructor}}
+
+struct BaseInaccessible { // #d-BaseInacc
+private:
+ ~BaseInaccessible(); // #d-BaseInacc-dtor
+};
+
+struct DerivedFromInaccessible : BaseInaccessible {}; // #d-DerivedInacc
+static_assert(__is_destructible(DerivedFromInaccessible));
+// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(destructible::DerivedFromInaccessible)'}} \
+// expected-note@-1 {{'destructible::DerivedFromInaccessible' is not destructible}} \
+// expected-note@-1 {{because it has a deleted destructor}}
+
+}
|
| def err_omp_incompatible_dyn_groupprivate_modifier | ||
| : Error<"modifier '%0' cannot be used along with modifier '%1' in " | ||
| "dyn_groupprivate">; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you remove their changes?
| static void DiagnoseNonDestructibleReason(Sema &SemaRef, SourceLocation Loc, | ||
| QualType T) { | ||
|
|
||
| QualType CoreT = T.getCanonicalType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does Core stand for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It stands for the core or canonical type, obtained using getCanonicalType(). We use it to analyze the underlying type (without typedefs or qualifiers) when determining if a type is destructible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't say a type is 'core' anyway, instead we use the word canonical or desugared
Are you using AI to answer questions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use it to analyze the underlying type (without typedefs or qualifiers) when determining if a type is destructible.
No we don't? I don't believe we've ever used that before. I'm also suspecting AI at play here, which is not acceptable if so, unless you completely understand what it has done. Please answer questions/make patches that you completely understand.
| if (CoreT->isIncompleteType()) { | ||
| SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) | ||
| << diag::TraitNotSatisfiedReason::IncompleteType; | ||
| return; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you test this branch?
Co-authored-by: Younan Zhang <zyn7109@gmail.com>
| def err_omp_incompatible_dyn_groupprivate_modifier | ||
| : Error<"modifier '%0' cannot be used along with modifier '%1' in " | ||
| "dyn_groupprivate">; | ||
| def err_omp_schedule_nonmonotonic_static : Error< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a really odd change, why are you moving this? Looking at 'main' this hasn't moved, so this doesn't appear to be a bad merge thing.
| static void DiagnoseNonDestructibleReason(Sema &SemaRef, SourceLocation Loc, | ||
| QualType T) { | ||
|
|
||
| QualType CoreT = T.getCanonicalType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use it to analyze the underlying type (without typedefs or qualifiers) when determining if a type is destructible.
No we don't? I don't believe we've ever used that before. I'm also suspecting AI at play here, which is not acceptable if so, unless you completely understand what it has done. Please answer questions/make patches that you completely understand.
| QualType T) { | ||
|
|
||
| QualType CoreT = T.getCanonicalType(); | ||
| if (const ArrayType *AT = SemaRef.Context.getAsArrayType(CoreT)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please comment on this to tell why this is appropriate.
| SemaRef.Diag(Loc, diag::note_unsatisfied_trait) | ||
| << CoreT << diag::TraitName::Destructible; | ||
|
|
||
| if (CoreT->isFunctionType()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is so much repetition on these next handful of branches. It looks like some sort of work shoudl be done to only do the SemaRef.Diag 1x and store the TraitNotSatisifedReason to then diagnose it.
|
|
||
| const CXXRecordDecl *Def = RD->getDefinition(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DO you have any examples that hit this branch? You very much should not be able to.
|
|
||
| if (Dtor->isDeleted()) { | ||
| SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) | ||
| << diag::TraitNotSatisfiedReason::DeletedDtr << 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does the 0 mean here?
This patch adds diagnostic reasoning for the std::is_destructible type trait in Clang’s Sema diagnostics, in response to issue #141911
It enables detailed “unsatisfied trait” messages when a type fails destructibility checks (e.g., void, function types, deleted/private destructors, incomplete types, etc).
Changes:
Added new tests to: