Skip to content

Commit 37664cd

Browse files
authored
[Clang] Implement diagnostics for why is_final is false (#154863)
Adds onto #141911
1 parent 77c5a65 commit 37664cd

File tree

4 files changed

+180
-2
lines changed

4 files changed

+180
-2
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,7 +1772,8 @@ def note_unsatisfied_trait
17721772
"%Replaceable{replaceable}|"
17731773
"%TriviallyCopyable{trivially copyable}|"
17741774
"%Empty{empty}|"
1775-
"%StandardLayout{standard-layout}"
1775+
"%StandardLayout{standard-layout}|"
1776+
"%Final{final}"
17761777
"}1">;
17771778

17781779
def note_unsatisfied_trait_reason
@@ -1815,7 +1816,9 @@ def note_unsatisfied_trait_reason
18151816
"%sub{select_special_member_kind}1}|"
18161817
"%FunctionType{is a function type}|"
18171818
"%CVVoidType{is a cv void type}|"
1818-
"%IncompleteArrayType{is an incomplete array type}"
1819+
"%IncompleteArrayType{is an incomplete array type}|"
1820+
"%NotClassOrUnion{is not a class or union type}|"
1821+
"%NotMarkedFinal{is not marked 'final'}"
18191822
"}0">;
18201823

18211824
def warn_consteval_if_always_true : Warning<

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1964,6 +1964,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
19641964
.Case("is_empty", TypeTrait::UTT_IsEmpty)
19651965
.Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout)
19661966
.Case("is_constructible", TypeTrait::TT_IsConstructible)
1967+
.Case("is_final", TypeTrait::UTT_IsFinal)
19671968
.Default(std::nullopt);
19681969
}
19691970

@@ -2448,6 +2449,52 @@ static void DiagnoseIsEmptyReason(Sema &S, SourceLocation Loc, QualType T) {
24482449
}
24492450
}
24502451

2452+
static void DiagnoseIsFinalReason(Sema &S, SourceLocation Loc,
2453+
const CXXRecordDecl *D) {
2454+
if (!D || D->isInvalidDecl())
2455+
return;
2456+
2457+
// Complete record but not 'final'.
2458+
if (!D->isEffectivelyFinal()) {
2459+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2460+
<< diag::TraitNotSatisfiedReason::NotMarkedFinal;
2461+
S.Diag(D->getLocation(), diag::note_defined_here) << D;
2462+
return;
2463+
}
2464+
}
2465+
2466+
static void DiagnoseIsFinalReason(Sema &S, SourceLocation Loc, QualType T) {
2467+
// Primary: “%0 is not final”
2468+
S.Diag(Loc, diag::note_unsatisfied_trait) << T << diag::TraitName::Final;
2469+
if (T->isReferenceType()) {
2470+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2471+
<< diag::TraitNotSatisfiedReason::Ref;
2472+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2473+
<< diag::TraitNotSatisfiedReason::NotClassOrUnion;
2474+
return;
2475+
}
2476+
// Arrays / functions / non-records → not a class/union.
2477+
if (S.Context.getAsArrayType(T)) {
2478+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2479+
<< diag::TraitNotSatisfiedReason::NotClassOrUnion;
2480+
return;
2481+
}
2482+
if (T->isFunctionType()) {
2483+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2484+
<< diag::TraitNotSatisfiedReason::FunctionType;
2485+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2486+
<< diag::TraitNotSatisfiedReason::NotClassOrUnion;
2487+
return;
2488+
}
2489+
if (!T->isRecordType()) {
2490+
S.Diag(Loc, diag::note_unsatisfied_trait_reason)
2491+
<< diag::TraitNotSatisfiedReason::NotClassOrUnion;
2492+
return;
2493+
}
2494+
if (const auto *D = T->getAsCXXRecordDecl())
2495+
DiagnoseIsFinalReason(S, Loc, D);
2496+
}
2497+
24512498
static bool hasMultipleDataBaseClassesWithFields(const CXXRecordDecl *D) {
24522499
int NumBasesWithFields = 0;
24532500
for (const CXXBaseSpecifier &Base : D->bases()) {
@@ -2624,6 +2671,15 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
26242671
case TT_IsConstructible:
26252672
DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
26262673
break;
2674+
case UTT_IsFinal: {
2675+
QualType QT = Args[0];
2676+
if (QT->isDependentType())
2677+
break;
2678+
const auto *RD = QT->getAsCXXRecordDecl();
2679+
if (!RD || !RD->isEffectivelyFinal())
2680+
DiagnoseIsFinalReason(*this, E->getBeginLoc(), QT); // unsatisfied
2681+
break;
2682+
}
26272683
default:
26282684
break;
26292685
}

clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ struct is_constructible {
5050

5151
template <typename... Args>
5252
constexpr bool is_constructible_v = __is_constructible(Args...);
53+
54+
template <typename T>
55+
struct is_final {
56+
static constexpr bool value = __is_final(T);
57+
};
58+
template <typename T>
59+
constexpr bool is_final_v = __is_final(T);
60+
5361
#endif
5462

5563
#ifdef STD2
@@ -116,6 +124,16 @@ using is_constructible = __details_is_constructible<Args...>;
116124

117125
template <typename... Args>
118126
constexpr bool is_constructible_v = __is_constructible(Args...);
127+
128+
template <typename T>
129+
struct __details_is_final {
130+
static constexpr bool value = __is_final(T);
131+
};
132+
template <typename T>
133+
using is_final = __details_is_final<T>;
134+
template <typename T>
135+
constexpr bool is_final_v = __is_final(T);
136+
119137
#endif
120138

121139

@@ -177,6 +195,14 @@ using is_constructible = __details_is_constructible<Args...>;
177195

178196
template <typename... Args>
179197
constexpr bool is_constructible_v = is_constructible<Args...>::value;
198+
199+
template <typename T>
200+
struct __details_is_final : bool_constant<__is_final(T)> {};
201+
template <typename T>
202+
using is_final = __details_is_final<T>;
203+
template <typename T>
204+
constexpr bool is_final_v = is_final<T>::value;
205+
180206
#endif
181207

182208
}
@@ -248,6 +274,31 @@ static_assert(std::is_constructible_v<void>);
248274
// expected-error@-1 {{static assertion failed due to requirement 'std::is_constructible_v<void>'}} \
249275
// expected-note@-1 {{because it is a cv void type}}
250276

277+
static_assert(!std::is_final<int>::value);
278+
279+
static_assert(std::is_final<int&>::value);
280+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_final<int &>::value'}} \
281+
// expected-note@-1 {{'int &' is not final}} \
282+
// expected-note@-1 {{because it is a reference type}} \
283+
// expected-note@-1 {{because it is not a class or union type}}
284+
285+
static_assert(std::is_final_v<int&>);
286+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_final_v<int &>'}} \
287+
// expected-note@-1 {{'int &' is not final}} \
288+
// expected-note@-1 {{because it is a reference type}} \
289+
// expected-note@-1 {{because it is not a class or union type}}
290+
291+
using Arr = int[3];
292+
static_assert(std::is_final<Arr>::value);
293+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_final<int[3]>::value'}} \
294+
// expected-note@-1 {{'Arr' (aka 'int[3]') is not final}} \
295+
// expected-note@-1 {{because it is not a class or union type}}
296+
297+
static_assert(std::is_final_v<Arr>);
298+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_final_v<int[3]>'}} \
299+
// expected-note@-1 {{'int[3]' is not final}} \
300+
// expected-note@-1 {{because it is not a class or union type}}
301+
251302
namespace test_namespace {
252303
using namespace std;
253304
static_assert(is_trivially_relocatable<int&>::value);
@@ -300,6 +351,31 @@ namespace test_namespace {
300351
static_assert(is_constructible_v<void>);
301352
// expected-error@-1 {{static assertion failed due to requirement 'is_constructible_v<void>'}} \
302353
// expected-note@-1 {{because it is a cv void type}}
354+
355+
static_assert(is_final<int&>::value);
356+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_final<int &>::value'}} \
357+
// expected-note@-1 {{'int &' is not final}} \
358+
// expected-note@-1 {{because it is a reference type}} \
359+
// expected-note@-1 {{because it is not a class or union type}}
360+
361+
static_assert(is_final_v<int&>);
362+
// expected-error@-1 {{static assertion failed due to requirement 'is_final_v<int &>'}} \
363+
// expected-note@-1 {{'int &' is not final}} \
364+
// expected-note@-1 {{because it is a reference type}} \
365+
// expected-note@-1 {{because it is not a class or union type}}
366+
367+
using A = int[2];
368+
static_assert(is_final<A>::value);
369+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_final<int[2]>::value'}} \
370+
// expected-note@-1 {{'A' (aka 'int[2]') is not final}} \
371+
// expected-note@-1 {{because it is not a class or union type}}
372+
373+
using Fn = void();
374+
static_assert(is_final<Fn>::value);
375+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_final<void ()>::value'}} \
376+
// expected-note@-1 {{'Fn' (aka 'void ()') is not final}} \
377+
// expected-note@-1 {{because it is a function type}} \
378+
// expected-note@-1 {{because it is not a class or union type}}
303379
}
304380

305381

clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,3 +829,46 @@ static_assert(__is_standard_layout(H)); // no diagnostics
829829
static_assert(__is_standard_layout(I)); // no diagnostics
830830
}
831831

832+
namespace is_final_tests {
833+
struct C {}; // #e-C
834+
static_assert(__is_final(C));
835+
// expected-error@-1 {{static assertion failed due to requirement '__is_final(is_final_tests::C)'}} \
836+
// expected-note@-1 {{'C' is not final}} \
837+
// expected-note@-1 {{because it is not marked 'final'}} \
838+
// expected-note@#e-C {{'C' defined here}}
839+
840+
union U {}; // #e-U
841+
static_assert(__is_final(U));
842+
// expected-error@-1 {{static assertion failed due to requirement '__is_final(is_final_tests::U)'}} \
843+
// expected-note@-1 {{'U' is not final}} \
844+
// expected-note@-1 {{because it is not marked 'final'}} \
845+
// expected-note@#e-U {{'U' defined here}}
846+
847+
// ----- non-class/union types -----
848+
using I = int;
849+
static_assert(__is_final(I));
850+
// expected-error@-1 {{static assertion failed due to requirement '__is_final(int)'}} \
851+
// expected-note@-1 {{'I' (aka 'int') is not final}} \
852+
// expected-note@-1 {{because it is not a class or union type}}
853+
854+
using Fty = void(); // function type
855+
static_assert(__is_final(Fty));
856+
// expected-error@-1 {{static assertion failed due to requirement '__is_final(void ())'}} \
857+
// expected-note@-1 {{'Fty' (aka 'void ()') is not final}} \
858+
// expected-note@-1 {{because it is a function type}} \
859+
// expected-note@-1 {{because it is not a class or union type}}
860+
861+
using Arr = int[3];
862+
static_assert(__is_final(Arr));
863+
// expected-error@-1 {{static assertion failed due to requirement '__is_final(int[3])'}} \
864+
// expected-note@-1 {{'Arr' (aka 'int[3]') is not final}} \
865+
// expected-note@-1 {{because it is not a class or union type}}
866+
867+
using Ref = int&;
868+
static_assert(__is_final(Ref));
869+
// expected-error@-1 {{static assertion failed due to requirement '__is_final(int &)'}} \
870+
// expected-note@-1 {{'Ref' (aka 'int &') is not final}} \
871+
// expected-note@-1 {{because it is a reference type}} \
872+
// expected-note@-1 {{because it is not a class or union type}}
873+
874+
}

0 commit comments

Comments
 (0)