Author: Samarth Narang
Date: 2025-08-22T09:50:37-04:00
New Revision: 37664cd991246aeba988b963d534cb10b4ab0681

URL: 
https://github.com/llvm/llvm-project/commit/37664cd991246aeba988b963d534cb10b4ab0681
DIFF: 
https://github.com/llvm/llvm-project/commit/37664cd991246aeba988b963d534cb10b4ab0681.diff

LOG: [Clang] Implement diagnostics for why is_final is false (#154863)

Adds onto https://github.com/llvm/llvm-project/issues/141911

Added: 
    

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaTypeTraits.cpp
    clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
    clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp

Removed: 
    


################################################################################
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}}
+
+}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to