Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<
Expand Down
56 changes: 56 additions & 0 deletions clang/lib/Sema/SemaTypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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;
}
Expand Down
76 changes: 76 additions & 0 deletions clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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

}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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}}
}


Expand Down
43 changes: 43 additions & 0 deletions clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}

}