-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[Clang] Implement diagnostics for why is_final is false #154863
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
Conversation
@llvm/pr-subscribers-clang Author: Samarth Narang (snarang181) ChangesAdds onto #141911 Full diff: https://github.com/llvm/llvm-project/pull/154863.diff 4 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f40cac865ade0..f50d245965648 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1772,7 +1772,8 @@ def note_unsatisfied_trait
"%Replaceable{replaceable}|"
"%TriviallyCopyable{trivially copyable}|"
"%Empty{empty}|"
- "%StandardLayout{standard-layout}"
+ "%StandardLayout{standard-layout}|"
+ "%Final{final}"
"}1">;
def note_unsatisfied_trait_reason
@@ -1815,7 +1816,9 @@ def note_unsatisfied_trait_reason
"%sub{select_special_member_kind}1}|"
"%FunctionType{is a function type}|"
"%CVVoidType{is a cv void type}|"
- "%IncompleteArrayType{is an incomplete array type}"
+ "%IncompleteArrayType{is an incomplete array type}|"
+ "%NotClassOrUnion{is not a class or union type}|"
+ "%NotMarkedFinal{is not marked 'final'}"
"}0">;
def warn_consteval_if_always_true : Warning<
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 9b9dd172003a0..0b4d5916f8dc3 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1964,6 +1964,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
.Case("is_empty", TypeTrait::UTT_IsEmpty)
.Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout)
.Case("is_constructible", TypeTrait::TT_IsConstructible)
+ .Case("is_final", TypeTrait::UTT_IsFinal)
.Default(std::nullopt);
}
@@ -2448,6 +2449,52 @@ static void DiagnoseIsEmptyReason(Sema &S, SourceLocation Loc, QualType T) {
}
}
+static void DiagnoseIsFinalReason(Sema &S, SourceLocation Loc,
+ const CXXRecordDecl *D) {
+ if (!D || D->isInvalidDecl())
+ return;
+
+ // Complete record but not 'final'.
+ if (!D->isEffectivelyFinal()) {
+ S.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NotMarkedFinal;
+ S.Diag(D->getLocation(), diag::note_defined_here) << D;
+ return;
+ }
+}
+
+static void DiagnoseIsFinalReason(Sema &S, SourceLocation Loc, QualType T) {
+ // Primary: “%0 is not final”
+ S.Diag(Loc, diag::note_unsatisfied_trait) << T << diag::TraitName::Final;
+ if (T->isReferenceType()) {
+ S.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::Ref;
+ S.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NotClassOrUnion;
+ return;
+ }
+ // Arrays / functions / non-records → not a class/union.
+ if (S.Context.getAsArrayType(T)) {
+ S.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NotClassOrUnion;
+ return;
+ }
+ if (T->isFunctionType()) {
+ S.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::FunctionType;
+ S.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NotClassOrUnion;
+ return;
+ }
+ if (!T->isRecordType()) {
+ S.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NotClassOrUnion;
+ return;
+ }
+ if (const auto *D = T->getAsCXXRecordDecl())
+ DiagnoseIsFinalReason(S, Loc, D);
+}
+
static bool hasMultipleDataBaseClassesWithFields(const CXXRecordDecl *D) {
int NumBasesWithFields = 0;
for (const CXXBaseSpecifier &Base : D->bases()) {
@@ -2624,6 +2671,15 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
case TT_IsConstructible:
DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
break;
+ case UTT_IsFinal: {
+ QualType QT = Args[0];
+ if (QT->isDependentType())
+ break;
+ const auto *RD = QT->getAsCXXRecordDecl();
+ if (!RD || !RD->isEffectivelyFinal())
+ DiagnoseIsFinalReason(*this, E->getBeginLoc(), QT); // unsatisfied
+ 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 f3ddbbfe15bdc..7c6c9ea4dde80 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -50,6 +50,14 @@ struct is_constructible {
template <typename... Args>
constexpr bool is_constructible_v = __is_constructible(Args...);
+
+template <typename T>
+struct is_final {
+ static constexpr bool value = __is_final(T);
+};
+template <typename T>
+constexpr bool is_final_v = __is_final(T);
+
#endif
#ifdef STD2
@@ -116,6 +124,16 @@ using is_constructible = __details_is_constructible<Args...>;
template <typename... Args>
constexpr bool is_constructible_v = __is_constructible(Args...);
+
+template <typename T>
+struct __details_is_final {
+ static constexpr bool value = __is_final(T);
+};
+template <typename T>
+using is_final = __details_is_final<T>;
+template <typename T>
+constexpr bool is_final_v = __is_final(T);
+
#endif
@@ -177,6 +195,14 @@ using is_constructible = __details_is_constructible<Args...>;
template <typename... Args>
constexpr bool is_constructible_v = is_constructible<Args...>::value;
+
+template <typename T>
+struct __details_is_final : bool_constant<__is_final(T)> {};
+template <typename T>
+using is_final = __details_is_final<T>;
+template <typename T>
+constexpr bool is_final_v = is_final<T>::value;
+
#endif
}
@@ -248,6 +274,31 @@ 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_final<int>::value);
+
+static_assert(std::is_final<int&>::value);
+// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_final<int &>::value'}} \
+// expected-note@-1 {{'int &' is not final}} \
+// expected-note@-1 {{because it is a reference type}} \
+// expected-note@-1 {{because it is not a class or union type}}
+
+static_assert(std::is_final_v<int&>);
+// expected-error@-1 {{static assertion failed due to requirement 'std::is_final_v<int &>'}} \
+// expected-note@-1 {{'int &' is not final}} \
+// expected-note@-1 {{because it is a reference type}} \
+// expected-note@-1 {{because it is not a class or union type}}
+
+using Arr = int[3];
+static_assert(std::is_final<Arr>::value);
+// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_final<int[3]>::value'}} \
+// expected-note@-1 {{'Arr' (aka 'int[3]') is not final}} \
+// expected-note@-1 {{because it is not a class or union type}}
+
+static_assert(std::is_final_v<Arr>);
+// expected-error@-1 {{static assertion failed due to requirement 'std::is_final_v<int[3]>'}} \
+// expected-note@-1 {{'int[3]' is not final}} \
+// expected-note@-1 {{because it is not a class or union type}}
+
namespace test_namespace {
using namespace std;
static_assert(is_trivially_relocatable<int&>::value);
@@ -300,6 +351,31 @@ 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_final<int&>::value);
+ // expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_final<int &>::value'}} \
+ // expected-note@-1 {{'int &' is not final}} \
+ // expected-note@-1 {{because it is a reference type}} \
+ // expected-note@-1 {{because it is not a class or union type}}
+
+ static_assert(is_final_v<int&>);
+ // expected-error@-1 {{static assertion failed due to requirement 'is_final_v<int &>'}} \
+ // expected-note@-1 {{'int &' is not final}} \
+ // expected-note@-1 {{because it is a reference type}} \
+ // expected-note@-1 {{because it is not a class or union type}}
+
+ using A = int[2];
+ static_assert(is_final<A>::value);
+ // expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_final<int[2]>::value'}} \
+ // expected-note@-1 {{'A' (aka 'int[2]') is not final}} \
+ // expected-note@-1 {{because it is not a class or union type}}
+
+ using Fn = void();
+ static_assert(is_final<Fn>::value);
+ // expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_final<void ()>::value'}} \
+ // expected-note@-1 {{'Fn' (aka 'void ()') is not final}} \
+ // expected-note@-1 {{because it is a function type}} \
+ // expected-note@-1 {{because it is not a class or union type}}
}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index 54806a93ddf80..1619b0b22b85f 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -829,3 +829,46 @@ static_assert(__is_standard_layout(H)); // no diagnostics
static_assert(__is_standard_layout(I)); // no diagnostics
}
+namespace is_final_tests {
+ struct C {}; // #e-C
+ static_assert(__is_final(C));
+ // expected-error@-1 {{static assertion failed due to requirement '__is_final(is_final_tests::C)'}} \
+ // expected-note@-1 {{'C' is not final}} \
+ // expected-note@-1 {{because it is not marked 'final'}} \
+ // expected-note@#e-C {{'C' defined here}}
+
+ union U {}; // #e-U
+ static_assert(__is_final(U));
+ // expected-error@-1 {{static assertion failed due to requirement '__is_final(is_final_tests::U)'}} \
+ // expected-note@-1 {{'U' is not final}} \
+ // expected-note@-1 {{because it is not marked 'final'}} \
+ // expected-note@#e-U {{'U' defined here}}
+
+ // ----- non-class/union types -----
+ using I = int;
+ static_assert(__is_final(I));
+ // expected-error@-1 {{static assertion failed due to requirement '__is_final(int)'}} \
+ // expected-note@-1 {{'I' (aka 'int') is not final}} \
+ // expected-note@-1 {{because it is not a class or union type}}
+
+ using Fty = void(); // function type
+ static_assert(__is_final(Fty));
+ // expected-error@-1 {{static assertion failed due to requirement '__is_final(void ())'}} \
+ // expected-note@-1 {{'Fty' (aka 'void ()') is not final}} \
+ // expected-note@-1 {{because it is a function type}} \
+ // expected-note@-1 {{because it is not a class or union type}}
+
+ using Arr = int[3];
+ static_assert(__is_final(Arr));
+ // expected-error@-1 {{static assertion failed due to requirement '__is_final(int[3])'}} \
+ // expected-note@-1 {{'Arr' (aka 'int[3]') is not final}} \
+ // expected-note@-1 {{because it is not a class or union type}}
+
+ using Ref = int&;
+ static_assert(__is_final(Ref));
+ // expected-error@-1 {{static assertion failed due to requirement '__is_final(int &)'}} \
+ // expected-note@-1 {{'Ref' (aka 'int &') is not final}} \
+ // expected-note@-1 {{because it is a reference type}} \
+ // expected-note@-1 {{because it is not a class or union type}}
+
+}
|
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.
Probably still worth a release note, else lgtm.
Thanks @erichkeane for reviewing. Linking #144161 (comment) where @cor3ntin mentioned we should have a single entry in |
Adds onto #141911