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