diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3e864475f22a1b..b8dc2a3a5a9891 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1776,7 +1776,8 @@ def note_unsatisfied_trait "%StandardLayout{standard-layout}|" "%Aggregate{aggregate}|" "%Final{final}|" - "%Abstract{abstract}" + "%Abstract{abstract}|" + "%Destructible{destructible}" "}1">; def note_unsatisfied_trait_reason @@ -1808,6 +1809,7 @@ def note_unsatisfied_trait_reason "%NonStandardLayoutMember{has a non-standard-layout member %1 of type %2}|" "%IndirectBaseWithFields{has an indirect base %1 with data members}|" "%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|" + "%InaccessibleDtr{has a %select{private|protected}1 destructor}|" "%UserProvidedCtr{has a user provided %select{copy|move}1 " "constructor}|" "%UserDeclaredCtr{has a user-declared constructor}|" @@ -1823,6 +1825,7 @@ def note_unsatisfied_trait_reason "%FunctionType{is a function type}|" "%CVVoidType{is a cv void type}|" "%IncompleteArrayType{is an incomplete array type}|" + "%IncompleteType{is an incomplete type}|" "%PrivateProtectedDirectDataMember{has a %select{private|protected}1 direct data member}|" "%PrivateProtectedDirectBase{has a %select{private|protected}1 direct base}|" "%NotClassOrUnion{is not a class or union type}|" @@ -12135,11 +12138,11 @@ def err_omp_linear_ordered : Error< "'linear' clause cannot be specified along with 'ordered' clause with a parameter">; def err_omp_unexpected_schedule_modifier : Error< "modifier '%0' cannot be used along with modifier '%1'">; -def err_omp_schedule_nonmonotonic_static : Error< - "'nonmonotonic' modifier can only be specified with 'dynamic' or 'guided' schedule kind">; def err_omp_incompatible_dyn_groupprivate_modifier : Error<"modifier '%0' cannot be used along with modifier '%1' in " "dyn_groupprivate">; +def err_omp_schedule_nonmonotonic_static : Error< + "'nonmonotonic' modifier can only be specified with 'dynamic' or 'guided' schedule kind">; def err_omp_simple_clause_incompatible_with_ordered : Error< "'%0' clause with '%1' modifier cannot be specified if an 'ordered' clause is specified">; def err_omp_ordered_simd : Error< diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 38877967af05e5..6751e364c39b3d 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -2028,6 +2028,7 @@ static std::optional StdNameToTypeTrait(StringRef Name) { .Case("is_constructible", TypeTrait::TT_IsConstructible) .Case("is_final", TypeTrait::UTT_IsFinal) .Case("is_abstract", TypeTrait::UTT_IsAbstract) + .Case("is_destructible", TypeTrait::UTT_IsDestructible) .Default(std::nullopt); } @@ -2399,6 +2400,66 @@ static void DiagnoseNonConstructibleReason( SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D; } +static void DiagnoseNonDestructibleReason(Sema &SemaRef, SourceLocation Loc, + QualType T) { + + QualType CoreT = T.getCanonicalType(); + if (const ArrayType *AT = SemaRef.Context.getAsArrayType(CoreT)) + CoreT = AT->getElementType(); + + SemaRef.Diag(Loc, diag::note_unsatisfied_trait) + << CoreT << diag::TraitName::Destructible; + + if (CoreT->isFunctionType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::FunctionType; + return; + } + + if (CoreT->isVoidType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::CVVoidType; + return; + } + + if (CoreT->isIncompleteType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::IncompleteType; + return; + } + + const CXXRecordDecl *RD = CoreT->getAsCXXRecordDecl(); + if (!RD || RD->isInvalidDecl()) + return; + + const CXXRecordDecl *Def = RD->getDefinition(); + if (!Def) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::IncompleteType; + return; + } + + CXXDestructorDecl *Dtor = Def->getDestructor(); + if (!Dtor) + return; + + if (Dtor->isDeleted()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::DeletedDtr << 0 + << Dtor->getSourceRange(); + return; + } + + AccessSpecifier AS = Dtor->getAccess(); + if (AS == AS_private || AS == AS_protected) { + unsigned Select = AS != AS_private; + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::InaccessibleDtr << Select + << Dtor->getSourceRange(); + return; + } +} + static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef, SourceLocation Loc, QualType T) { SemaRef.Diag(Loc, diag::note_unsatisfied_trait) @@ -2889,6 +2950,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) { case TT_IsConstructible: DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args); break; + case UTT_IsDestructible: + DiagnoseNonDestructibleReason(*this, E->getBeginLoc(), Args[0]); + break; case UTT_IsAggregate: DiagnoseNonAggregateReason(*this, E->getBeginLoc(), Args[0]); break; diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp index 3e03a79275232f..3e02fe8f10f560 100644 --- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp +++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp @@ -73,6 +73,15 @@ struct is_abstract { template constexpr bool is_abstract_v = __is_abstract(T); +template +struct is_destructible { + static constexpr bool value = __is_destructible(T); +}; + +template +constexpr bool is_destructible_v = __is_destructible(T); + + #endif #ifdef STD2 @@ -167,6 +176,17 @@ using is_abstract = __details_is_abstract; template constexpr bool is_abstract_v = __is_abstract(T); +template +struct __details_is_destructible { + static constexpr bool value = __is_destructible(T); +}; + +template +using is_destructible = __details_is_destructible; + +template +constexpr bool is_destructible_v = __is_destructible(T); + #endif @@ -252,6 +272,15 @@ using is_abstract = __details_is_abstract; template constexpr bool is_abstract_v = is_abstract::value; +template +struct __details_is_destructible : bool_constant<__is_destructible(T)> {}; + +template +using is_destructible = __details_is_destructible; + +template +constexpr bool is_destructible_v = is_destructible::value; + #endif } @@ -374,6 +403,18 @@ static_assert(std::is_abstract_v); // expected-note@-1 {{because it is a reference type}} \ // expected-note@-1 {{because it is not a struct or class type}} +static_assert(std::is_destructible::value); + +static_assert(std::is_destructible::value); +// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_destructible::value'}} \ +// expected-note@-1 {{'void' is not destructible}} \ +// expected-note@-1 {{because it is a cv void type}} + +static_assert(std::is_destructible_v); +// expected-error@-1 {{static assertion failed due to requirement 'std::is_destructible_v'}} \ +// expected-note@-1 {{'void' is not destructible}} \ +// expected-note@-1 {{because it is a cv void type}} + namespace test_namespace { using namespace std; @@ -473,6 +514,17 @@ namespace test_namespace { // expected-note@-1 {{'int &' is not abstract}} \ // expected-note@-1 {{because it is a reference type}} \ // expected-note@-1 {{because it is not a struct or class type}} + + static_assert(is_destructible::value); + // expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_destructible::value'}} \ + // expected-note@-1 {{'void' is not destructible}} \ + // expected-note@-1 {{because it is a cv void type}} + + static_assert(is_destructible_v); + // expected-error@-1 {{static assertion failed due to requirement 'is_destructible_v'}} \ + // expected-note@-1 {{'void' is not destructible}} \ + // expected-note@-1 {{because it is a cv void type}} + } @@ -518,6 +570,15 @@ concept C5 = std::is_aggregate_v; // #concept10 template void g5(); // #cand10 +template +requires std::is_destructible::value void f6(); // #cand11 + +template +concept C6 = std::is_destructible_v; // #concept11 + +template void g6(); // #cand12 + + void test() { f(); // expected-error@-1 {{no matching function for call to 'f'}} \ @@ -589,6 +650,21 @@ void test() { // expected-note@#concept10 {{because 'std::is_aggregate_v' evaluated to false}} \ // expected-note@#concept10 {{'void' is not aggregate}} \ // expected-note@#concept10 {{because it is a cv void type}} + + f6(); + // expected-error@-1 {{no matching function for call to 'f6'}} \ + // expected-note@#cand11 {{candidate template ignored: constraints not satisfied [with T = void]}} \ + // expected-note-re@#cand11 {{because '{{.*}}is_destructible::value' evaluated to false}} \ + // expected-note@#cand11 {{'void' is not destructible}} \ + // expected-note@#cand11 {{because it is a cv void type}} + + g6(); + // expected-error@-1 {{no matching function for call to 'g6'}} \ + // expected-note@#cand12 {{candidate template ignored: constraints not satisfied [with T = void]}} \ + // expected-note@#cand12 {{because 'void' does not satisfy 'C6'}} \ + // expected-note@#concept11 {{because 'std::is_destructible_v' evaluated to false}} \ + // expected-note@#concept11 {{'void' is not destructible}} \ + // expected-note@#concept11 {{because it is a cv void type}} } } diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp index 22740418f09f56..858a5cc24868fb 100644 --- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp +++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp @@ -1052,3 +1052,52 @@ static_assert(__is_abstract(U)); // expected-note@-1 {{because it is not a struct or class type}} } +namespace destructible { + +struct Incomplete; // expected-note {{forward declaration of 'destructible::Incomplete'}} +static_assert(__is_destructible(Incomplete)); +// expected-error@-1 {{incomplete type 'Incomplete' used in type trait expression}} + +static_assert(__is_destructible(void)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(void)'}} \ +// expected-note@-1 {{'void' is not destructible}} \ +// expected-note@-1 {{because it is a cv void type}} + +using F = void(); +static_assert(__is_destructible(F)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(void ())'}} \ +// expected-note@-1 {{'void ()' is not destructible}} \ +// expected-note@-1 {{because it is a function type}} + +using Ref = int&; +static_assert(__is_destructible(Ref)); // no diagnostics (true) + +struct DeletedDtor { // #d-DeletedDtor + ~DeletedDtor() = delete; +}; +static_assert(__is_destructible(DeletedDtor)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(destructible::DeletedDtor)'}} \ +// expected-note@-1 {{'destructible::DeletedDtor' is not destructible}} \ +// expected-note@-1 {{because it has a deleted destructor}} + +struct PrivateDtor { // #d-PrivateDtor +private: + ~PrivateDtor(); // #d-PrivateDtor-dtor +}; +static_assert(__is_destructible(PrivateDtor)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(destructible::PrivateDtor)'}} \ +// expected-note@-1 {{'destructible::PrivateDtor' is not destructible}} \ +// expected-note@-1 {{because it has a private destructor}} + +struct BaseInaccessible { // #d-BaseInacc +private: + ~BaseInaccessible(); // #d-BaseInacc-dtor +}; + +struct DerivedFromInaccessible : BaseInaccessible {}; // #d-DerivedInacc +static_assert(__is_destructible(DerivedFromInaccessible)); +// expected-error@-1 {{static assertion failed due to requirement '__is_destructible(destructible::DerivedFromInaccessible)'}} \ +// expected-note@-1 {{'destructible::DerivedFromInaccessible' is not destructible}} \ +// expected-note@-1 {{because it has a deleted destructor}} + +}