Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
10 changes: 9 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1767,7 +1767,8 @@ def note_unsatisfied_trait
: Note<"%0 is not %enum_select<TraitName>{"
"%TriviallyRelocatable{trivially relocatable}|"
"%Replaceable{replaceable}|"
"%TriviallyCopyable{trivially copyable}"
"%TriviallyCopyable{trivially copyable}|"
"%StandardLayout{standard-layout}"
"}1">;

def note_unsatisfied_trait_reason
Expand All @@ -1787,6 +1788,13 @@ def note_unsatisfied_trait_reason
"%NonReplaceableField{has a non-replaceable member %1 of type %2}|"
"%NTCBase{has a non-trivially-copyable base %1}|"
"%NTCField{has a non-trivially-copyable member %1 of type %2}|"
"%NonStandardLayoutBase{has a non-standard-layout base %1}|"
"%MixedAccess{has mixed access specifiers}|"
"%MixedAccessField{field %1 has a different access specifier than field %2}|"
"%MultipleDataBase{has multiple base classes with data members}|"
"%VirtualFunction{has a virtual function}|"
"%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}|"
"%UserProvidedCtr{has a user provided %select{copy|move}1 "
"constructor}|"
Expand Down
137 changes: 137 additions & 0 deletions clang/lib/Sema/SemaTypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1958,6 +1958,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
.Case("is_assignable", TypeTrait::BTT_IsAssignable)
.Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout)
.Default(std::nullopt);
}

Expand Down Expand Up @@ -2313,6 +2314,139 @@ static void DiagnoseNonAssignableReason(Sema &SemaRef, SourceLocation Loc,
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}

static bool hasMultipleDataBaseClassesWithFields(const CXXRecordDecl *D) {
int NumBasesWithFields = 0;
for (const CXXBaseSpecifier &Base : D->bases()) {
const CXXRecordDecl *BaseRD = Base.getType()->getAsCXXRecordDecl();
if (!BaseRD || BaseRD->isInvalidDecl())
continue;

for (const FieldDecl *Field : BaseRD->fields()) {
if (!Field->isUnnamedBitField()) {
if (++NumBasesWithFields > 1)
return true; // found more than one base class with fields
break; // no need to check further fields in this base class
}
}
}
return false;
}

static void DiagnoseNonStandardLayoutReason(Sema &SemaRef, SourceLocation Loc,
const CXXRecordDecl *D) {
for (const CXXBaseSpecifier &B : D->bases()) {
assert(B.getType()->getAsCXXRecordDecl() && "invalid base?");
if (B.isVirtual()) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::VBase << B.getType()
<< B.getSourceRange();
}
if (!B.getType()->isStandardLayoutType()) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::NonStandardLayoutBase << B.getType()
<< B.getSourceRange();
}
}
// Check for mixed access specifiers in fields.
const FieldDecl *FirstField = nullptr;
AccessSpecifier FirstAccess = AS_none;

for (const FieldDecl *Field : D->fields()) {
if (Field->isUnnamedBitField())
continue;

// Record the first field we see
if (!FirstField) {
FirstField = Field;
FirstAccess = Field->getAccess();
continue;
}

// Check if the field has a different access specifier than the first one.
if (Field->getAccess() != FirstAccess) {
// Emit a diagnostic about mixed access specifiers.
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::MixedAccess;

SemaRef.Diag(FirstField->getLocation(), diag::note_defined_here)
<< FirstField;

SemaRef.Diag(Field->getLocation(), diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::MixedAccessField << Field
<< FirstField;

// No need to check further fields, as we already found mixed access.
break;
}
}
if (hasMultipleDataBaseClassesWithFields(D)) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::MultipleDataBase;
}
if (D->isPolymorphic()) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::VirtualFunction;

for (const CXXMethodDecl *Method : D->methods()) {
if (Method->isVirtual()) {
SemaRef.Diag(Method->getLocation(), diag::note_defined_here) << Method;
break;
}
}
}
for (const FieldDecl *Field : D->fields()) {
if (!Field->getType()->isStandardLayoutType()) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::NonStandardLayoutMember << Field
<< Field->getType() << Field->getSourceRange();
}
}
// Find any indirect base classes that have fields.
if (D->hasDirectFields()) {
const CXXRecordDecl *Indirect = nullptr;
D->forallBases([&](const CXXRecordDecl *BaseDef) {
if (BaseDef->hasDirectFields()) {
Indirect = BaseDef;
return false; // stop traversal
}
return true; // continue to the next base
});
if (Indirect) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::IndirectBaseWithFields << Indirect
<< Indirect->getSourceRange();
}
}
}

static void DiagnoseNonStandardLayoutReason(Sema &SemaRef, SourceLocation Loc,
QualType T) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
<< T << diag::TraitName::StandardLayout;

// Check type-level exclusion first.
if (T->isVariablyModifiedType()) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::VLA;
return;
}

if (T->isReferenceType()) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::Ref;
return;
}
T = T.getNonReferenceType();
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
if (!D || D->isInvalidDecl())
return;

if (D->hasDefinition())
DiagnoseNonStandardLayoutReason(SemaRef, Loc, D);

SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}

void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
E = E->IgnoreParenImpCasts();
if (E->containsErrors())
Expand All @@ -2336,6 +2470,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
case BTT_IsAssignable:
DiagnoseNonAssignableReason(*this, E->getBeginLoc(), Args[0], Args[1]);
break;
case UTT_IsStandardLayout:
DiagnoseNonStandardLayoutReason(*this, E->getBeginLoc(), Args[0]);
break;
default:
break;
}
Expand Down
50 changes: 50 additions & 0 deletions clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ struct is_assignable {

template <typename T, typename U>
constexpr bool is_assignable_v = __is_assignable(T, U);

template <typename T>
struct is_standard_layout {
static constexpr bool value = __is_standard_layout(T);
};
template <typename T>
constexpr bool is_standard_layout_v = __is_standard_layout(T);
#endif

#ifdef STD2
Expand Down Expand Up @@ -63,6 +70,17 @@ using is_assignable = __details_is_assignable<T, U>;

template <typename T, typename U>
constexpr bool is_assignable_v = __is_assignable(T, U);

template <typename T>
struct __details_is_standard_layout {
static constexpr bool value = __is_standard_layout(T);


};
template <typename T>
using is_standard_layout = __details_is_standard_layout<T>;
template <typename T>
constexpr bool is_standard_layout_v = __is_standard_layout(T);
#endif


Expand Down Expand Up @@ -101,6 +119,13 @@ using is_assignable = __details_is_assignable<T, U>;

template <typename T, typename U>
constexpr bool is_assignable_v = is_assignable<T, U>::value;

template <typename T>
struct __details_is_standard_layout : bool_constant<__is_standard_layout(T)> {};
template <typename T>
using is_standard_layout = __details_is_standard_layout<T>;
template <typename T>
constexpr bool is_standard_layout_v = is_standard_layout<T>::value;
#endif

}
Expand All @@ -127,6 +152,21 @@ static_assert(std::is_trivially_copyable_v<int&>);
// expected-note@-1 {{'int &' is not trivially copyable}} \
// expected-note@-1 {{because it is a reference type}}


// Direct tests
static_assert(std::is_standard_layout<int>::value);
static_assert(std::is_standard_layout_v<int>);

static_assert(std::is_standard_layout<int&>::value);
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_standard_layout<int &>::value'}} \
// expected-note@-1 {{'int &' is not standard-layout}} \
// expected-note@-1 {{because it is a reference type}}

static_assert(std::is_standard_layout_v<int&>);
// expected-error@-1 {{static assertion failed due to requirement 'std::is_standard_layout_v<int &>'}} \
// expected-note@-1 {{'int &' is not standard-layout}} \
// expected-note@-1 {{because it is a reference type}}

static_assert(std::is_assignable<int&, int>::value);

static_assert(std::is_assignable<int&, void>::value);
Expand Down Expand Up @@ -156,6 +196,16 @@ namespace test_namespace {
// expected-note@-1 {{'int &' is not trivially copyable}} \
// expected-note@-1 {{because it is a reference type}}

static_assert(is_standard_layout<int&>::value);
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_standard_layout<int &>::value'}} \
// expected-note@-1 {{'int &' is not standard-layout}} \
// expected-note@-1 {{because it is a reference type}}

static_assert(is_standard_layout_v<int&>);
// expected-error@-1 {{static assertion failed due to requirement 'is_standard_layout_v<int &>'}} \
// expected-note@-1 {{'int &' is not standard-layout}} \
// expected-note@-1 {{because it is a reference type}}

static_assert(is_assignable<int&, void>::value);
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_assignable<int &, void>::value'}} \
// expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
Expand Down
Loading