Skip to content
Open
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
9 changes: 6 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}|"
Expand All @@ -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}|"
Expand Down Expand Up @@ -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<
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really odd change, why are you moving this? Looking at 'main' this hasn't moved, so this doesn't appear to be a bad merge thing.

"'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<
Expand Down
64 changes: 64 additions & 0 deletions clang/lib/Sema/SemaTypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2028,6 +2028,7 @@ static std::optional<TypeTrait> 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);
}

Expand Down Expand Up @@ -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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does Core stand for?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It stands for the core or canonical type, obtained using getCanonicalType(). We use it to analyze the underlying type (without typedefs or qualifiers) when determining if a type is destructible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't say a type is 'core' anyway, instead we use the word canonical or desugared

Are you using AI to answer questions?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use it to analyze the underlying type (without typedefs or qualifiers) when determining if a type is destructible.

No we don't? I don't believe we've ever used that before. I'm also suspecting AI at play here, which is not acceptable if so, unless you completely understand what it has done. Please answer questions/make patches that you completely understand.

if (const ArrayType *AT = SemaRef.Context.getAsArrayType(CoreT))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please comment on this to tell why this is appropriate.

CoreT = AT->getElementType();

SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
<< CoreT << diag::TraitName::Destructible;

if (CoreT->isFunctionType()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is so much repetition on these next handful of branches. It looks like some sort of work shoudl be done to only do the SemaRef.Diag 1x and store the TraitNotSatisifedReason to then diagnose it.

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;
}
Comment on lines +2425 to +2429
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you test this branch?


const CXXRecordDecl *RD = CoreT->getAsCXXRecordDecl();
if (!RD || RD->isInvalidDecl())
return;

const CXXRecordDecl *Def = RD->getDefinition();
Comment on lines +2434 to +2435
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DO you have any examples that hit this branch? You very much should not be able to.

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the 0 mean here?

<< 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)
Expand Down Expand Up @@ -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;
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 @@ -73,6 +73,15 @@ struct is_abstract {
template <typename T>
constexpr bool is_abstract_v = __is_abstract(T);

template <typename T>
struct is_destructible {
static constexpr bool value = __is_destructible(T);
};

template <typename T>
constexpr bool is_destructible_v = __is_destructible(T);


#endif

#ifdef STD2
Expand Down Expand Up @@ -167,6 +176,17 @@ using is_abstract = __details_is_abstract<T>;
template <typename T>
constexpr bool is_abstract_v = __is_abstract(T);

template <typename T>
struct __details_is_destructible {
static constexpr bool value = __is_destructible(T);
};

template <typename T>
using is_destructible = __details_is_destructible<T>;

template <typename T>
constexpr bool is_destructible_v = __is_destructible(T);

#endif


Expand Down Expand Up @@ -252,6 +272,15 @@ using is_abstract = __details_is_abstract<T>;
template <typename T>
constexpr bool is_abstract_v = is_abstract<T>::value;

template <typename T>
struct __details_is_destructible : bool_constant<__is_destructible(T)> {};

template <typename T>
using is_destructible = __details_is_destructible<T>;

template <typename T>
constexpr bool is_destructible_v = is_destructible<T>::value;

#endif
}

Expand Down Expand Up @@ -374,6 +403,18 @@ static_assert(std::is_abstract_v<int&>);
// 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<int>::value);

static_assert(std::is_destructible<void>::value);
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_destructible<void>::value'}} \
// expected-note@-1 {{'void' is not destructible}} \
// expected-note@-1 {{because it is a cv void type}}

static_assert(std::is_destructible_v<void>);
// expected-error@-1 {{static assertion failed due to requirement 'std::is_destructible_v<void>'}} \
// expected-note@-1 {{'void' is not destructible}} \
// expected-note@-1 {{because it is a cv void type}}


namespace test_namespace {
using namespace std;
Expand Down Expand Up @@ -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<void>::value);
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_destructible<void>::value'}} \
// expected-note@-1 {{'void' is not destructible}} \
// expected-note@-1 {{because it is a cv void type}}

static_assert(is_destructible_v<void>);
// expected-error@-1 {{static assertion failed due to requirement 'is_destructible_v<void>'}} \
// expected-note@-1 {{'void' is not destructible}} \
// expected-note@-1 {{because it is a cv void type}}

}


Expand Down Expand Up @@ -518,6 +570,15 @@ concept C5 = std::is_aggregate_v<T>; // #concept10

template <C5 T> void g5(); // #cand10

template <typename T>
requires std::is_destructible<T>::value void f6(); // #cand11

template <typename T>
concept C6 = std::is_destructible_v<T>; // #concept11

template <C6 T> void g6(); // #cand12


void test() {
f<int&>();
// expected-error@-1 {{no matching function for call to 'f'}} \
Expand Down Expand Up @@ -589,6 +650,21 @@ void test() {
// expected-note@#concept10 {{because 'std::is_aggregate_v<void>' evaluated to false}} \
// expected-note@#concept10 {{'void' is not aggregate}} \
// expected-note@#concept10 {{because it is a cv void type}}

f6<void>();
// 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<void>::value' evaluated to false}} \
// expected-note@#cand11 {{'void' is not destructible}} \
// expected-note@#cand11 {{because it is a cv void type}}

g6<void>();
// 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<void>' evaluated to false}} \
// expected-note@#concept11 {{'void' is not destructible}} \
// expected-note@#concept11 {{because it is a cv void type}}
}
}

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

}