Skip to content
Closed
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
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,9 @@ def note_unsatisfied_trait
: Note<"%0 is not %enum_select<TraitName>{"
"%TriviallyRelocatable{trivially relocatable}|"
"%Replaceable{replaceable}|"
"%TriviallyCopyable{trivially copyable}"
"%TriviallyCopyable{trivially copyable}|"
"%Constructible{constructible with provided types}|"
"%StandardLayout{standard-layout}"
"}1">;

def note_unsatisfied_trait_reason
Expand All @@ -1787,6 +1789,12 @@ 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}|"
"%NonStdLayoutBase{has a non-standard-layout base %1}|"
"%MixedAccess{has mixed access specifiers}|"
"%MultipleDataBase{has multiple base classes with data members}|"
"%VirtualFunction{has virtual functions}|"
"%NonStdLayoutMember{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
208 changes: 203 additions & 5 deletions clang/lib/Sema/SemaTypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +1947,8 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
TypeTrait::UTT_IsCppTriviallyRelocatable)
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
.Case("is_constructible", TypeTrait::TT_IsConstructible)
.Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout)
.Default(std::nullopt);
}

Expand Down Expand Up @@ -2257,21 +2259,211 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
}
}

static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
SourceLocation Loc, QualType T) {
static void DiagnoseNonConstructibleReason(
Sema &SemaRef, SourceLocation Loc,
const llvm::SmallVector<clang::QualType, 1> &Ts) {
if (Ts.empty()) {
return;
}

bool ContainsVoid = false;
for (const QualType &ArgTy : Ts) {
ContainsVoid |= ArgTy->isVoidType();
}

if (ContainsVoid)
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::CVVoidType;

QualType T = Ts[0];
if (T->isFunctionType())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::FunctionType;

if (T->isIncompleteArrayType())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::IncompleteArrayType;

const CXXRecordDecl *D = T->getAsCXXRecordDecl();
if (!D || D->isInvalidDecl() || !D->hasDefinition())
return;

llvm::BumpPtrAllocator OpaqueExprAllocator;
SmallVector<Expr *, 2> ArgExprs;
ArgExprs.reserve(Ts.size() - 1);
for (unsigned I = 1, N = Ts.size(); I != N; ++I) {
QualType ArgTy = Ts[I];
if (ArgTy->isObjectType() || ArgTy->isFunctionType())
ArgTy = SemaRef.Context.getRValueReferenceType(ArgTy);
ArgExprs.push_back(
new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
OpaqueValueExpr(Loc, ArgTy.getNonLValueExprType(SemaRef.Context),
Expr::getValueKindForType(ArgTy)));
}

EnterExpressionEvaluationContext Unevaluated(
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::ContextRAII TUContext(SemaRef,
SemaRef.Context.getTranslationUnitDecl());
InitializedEntity To(InitializedEntity::InitializeTemporary(T));
InitializationKind InitKind(InitializationKind::CreateDirect(Loc, Loc, Loc));
InitializationSequence Init(SemaRef, To, InitKind, ArgExprs);

Init.Diagnose(SemaRef, To, InitKind, ArgExprs);
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}

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

if (T->isReferenceType())
// 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;
}

static bool hasMixedAccessSpecifier(const CXXRecordDecl *D) {
AccessSpecifier FirstAccess = AS_none;
for (const FieldDecl *Field : D->fields()) {
if (Field->isUnnamedBitField())
continue;
AccessSpecifier FieldAccess = Field->getAccess();
if (FirstAccess == AS_none) {
FirstAccess = FieldAccess;
} else if (FieldAccess != FirstAccess) {
return true;
}
}
return false;
}

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()) {
++NumBasesWithFields;
break; // Only count the base once.
}
}
}
return NumBasesWithFields > 1;
}

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::NonStdLayoutBase << B.getType()
<< B.getSourceRange();
}
}
if (hasMixedAccessSpecifier(D)) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::MixedAccess;
}
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 FieldDecl *Field : D->fields()) {
if (!Field->getType()->isStandardLayoutType()) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::NonStdLayoutMember << Field
<< Field->getType() << Field->getSourceRange();
}
}

// if this class and an indirect base
// both have non-static data members, grab the first such base.
if (D->hasDirectFields()) {
SmallVector<const CXXRecordDecl *, 4> Records;

// Recursive lambda to collect all bases that declare fields
std::function<void(const CXXRecordDecl *)> collect =
[&](const CXXRecordDecl *R) {
for (const CXXBaseSpecifier &B : R->bases()) {
const auto *BR = B.getType()->getAsCXXRecordDecl();
if (!BR || !BR->hasDefinition())
continue;
if (BR->hasDirectFields())
Records.push_back(BR);
// Recurse into the base class.
collect(BR);
}
};

// Collect all bases that declare fields.
collect(D);

// If more than one record has fields, then the layout is non-standard.
if (!Records.empty()) {
const CXXRecordDecl *Indirect = Records.front();
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())
DiagnoseNonTriviallyCopyableReason(SemaRef, Loc, D);
DiagnoseNonStandardLayoutReason(SemaRef, Loc, D);

SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}
Expand All @@ -2296,6 +2488,12 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
case UTT_IsTriviallyCopyable:
DiagnoseNonTriviallyCopyableReason(*this, E->getBeginLoc(), Args[0]);
break;
case TT_IsConstructible:
DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
break;
case UTT_IsStandardLayout:
DiagnoseNonStandardLayoutReason(*this, E->getBeginLoc(), Args[0]);
break;
default:
break;
}
Expand Down
95 changes: 95 additions & 0 deletions clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ struct is_trivially_copyable {

template <typename T>
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);

template <typename... Args>
struct is_constructible {
static constexpr bool value = __is_constructible(Args...);
};

template <typename... Args>
constexpr bool is_constructible_v = __is_constructible(Args...);

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 All @@ -44,6 +59,26 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>;

template <typename T>
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);

template <typename... Args>
struct __details_is_constructible{
static constexpr bool value = __is_constructible(Args...);
};

template <typename... Args>
using is_constructible = __details_is_constructible<Args...>;

template <typename... Args>
constexpr bool is_constructible_v = __is_constructible(Args...);

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 @@ -73,6 +108,24 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>;

template <typename T>
constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;

template <typename... Args>
struct __details_is_constructible : bool_constant<__is_constructible(Args...)> {};

template <typename... Args>
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_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 Down Expand Up @@ -100,6 +153,30 @@ static_assert(std::is_trivially_copyable_v<int&>);
// 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_constructible<int, int>::value);

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

namespace test_namespace {
using namespace std;
static_assert(is_trivially_relocatable<int&>::value);
Expand All @@ -119,6 +196,24 @@ namespace test_namespace {
// expected-error@-1 {{static assertion failed due to requirement 'is_trivially_copyable_v<int &>'}} \
// expected-note@-1 {{'int &' is not trivially copyable}} \
// expected-note@-1 {{because it is a reference type}}

static_assert(is_constructible<void>::value);
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_constructible<void>::value'}} \
// expected-note@-1 {{because it is a cv void type}}
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_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}}

}


Expand Down
Loading
Loading