Skip to content

Commit 6f787e1

Browse files
committed
Add std layout diagnostics
Add diagnostic test cases
1 parent f62a8ab commit 6f787e1

File tree

4 files changed

+420
-6
lines changed

4 files changed

+420
-6
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1767,7 +1767,9 @@ def note_unsatisfied_trait
17671767
: Note<"%0 is not %enum_select<TraitName>{"
17681768
"%TriviallyRelocatable{trivially relocatable}|"
17691769
"%Replaceable{replaceable}|"
1770-
"%TriviallyCopyable{trivially copyable}"
1770+
"%TriviallyCopyable{trivially copyable}|"
1771+
"%Constructible{constructible with provided types}|"
1772+
"%StandardLayout{standard-layout}"
17711773
"}1">;
17721774

17731775
def note_unsatisfied_trait_reason
@@ -1787,6 +1789,11 @@ def note_unsatisfied_trait_reason
17871789
"%NonReplaceableField{has a non-replaceable member %1 of type %2}|"
17881790
"%NTCBase{has a non-trivially-copyable base %1}|"
17891791
"%NTCField{has a non-trivially-copyable member %1 of type %2}|"
1792+
"%NonStdLayoutBase{has a non-standard-layout base %1}|"
1793+
"%MixedAccess{has mixed access specifiers}|"
1794+
"%MultipleDataBase{has multiple base classes with data members}|"
1795+
"%VirtualFunction{has virtual functions}|"
1796+
"%NonStdLayoutMember{has a non-standard-layout member %1 of type %2}|"
17901797
"%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
17911798
"%UserProvidedCtr{has a user provided %select{copy|move}1 "
17921799
"constructor}|"

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 172 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,6 +1947,8 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
19471947
TypeTrait::UTT_IsCppTriviallyRelocatable)
19481948
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
19491949
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
1950+
.Case("is_constructible", TypeTrait::TT_IsConstructible)
1951+
.Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout)
19501952
.Default(std::nullopt);
19511953
}
19521954

@@ -2257,21 +2259,180 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
22572259
}
22582260
}
22592261

2260-
static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
2261-
SourceLocation Loc, QualType T) {
2262+
static void DiagnoseNonConstructibleReason(
2263+
Sema &SemaRef, SourceLocation Loc,
2264+
const llvm::SmallVector<clang::QualType, 1> &Ts) {
2265+
if (Ts.empty()) {
2266+
return;
2267+
}
2268+
2269+
bool ContainsVoid = false;
2270+
for (const QualType &ArgTy : Ts) {
2271+
ContainsVoid |= ArgTy->isVoidType();
2272+
}
2273+
2274+
if (ContainsVoid)
2275+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2276+
<< diag::TraitNotSatisfiedReason::CVVoidType;
2277+
2278+
QualType T = Ts[0];
2279+
if (T->isFunctionType())
2280+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2281+
<< diag::TraitNotSatisfiedReason::FunctionType;
2282+
2283+
if (T->isIncompleteArrayType())
2284+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2285+
<< diag::TraitNotSatisfiedReason::IncompleteArrayType;
2286+
2287+
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
2288+
if (!D || D->isInvalidDecl() || !D->hasDefinition())
2289+
return;
2290+
2291+
llvm::BumpPtrAllocator OpaqueExprAllocator;
2292+
SmallVector<Expr *, 2> ArgExprs;
2293+
ArgExprs.reserve(Ts.size() - 1);
2294+
for (unsigned I = 1, N = Ts.size(); I != N; ++I) {
2295+
QualType ArgTy = Ts[I];
2296+
if (ArgTy->isObjectType() || ArgTy->isFunctionType())
2297+
ArgTy = SemaRef.Context.getRValueReferenceType(ArgTy);
2298+
ArgExprs.push_back(
2299+
new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
2300+
OpaqueValueExpr(Loc, ArgTy.getNonLValueExprType(SemaRef.Context),
2301+
Expr::getValueKindForType(ArgTy)));
2302+
}
2303+
2304+
EnterExpressionEvaluationContext Unevaluated(
2305+
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
2306+
Sema::ContextRAII TUContext(SemaRef,
2307+
SemaRef.Context.getTranslationUnitDecl());
2308+
InitializedEntity To(InitializedEntity::InitializeTemporary(T));
2309+
InitializationKind InitKind(InitializationKind::CreateDirect(Loc, Loc, Loc));
2310+
InitializationSequence Init(SemaRef, To, InitKind, ArgExprs);
2311+
2312+
Init.Diagnose(SemaRef, To, InitKind, ArgExprs);
2313+
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
2314+
}
2315+
2316+
static void DiagnoseNonStandardLayoutReason(Sema &SemaRef, SourceLocation Loc,
2317+
QualType T) {
22622318
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
2263-
<< T << diag::TraitName::TriviallyCopyable;
2319+
<< T << diag::TraitName::StandardLayout;
22642320

2265-
if (T->isReferenceType())
2321+
// Check type-level exclusion first
2322+
if (T->isVariablyModifiedType()) {
2323+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2324+
<< diag::TraitNotSatisfiedReason::VLA;
2325+
return;
2326+
}
2327+
2328+
if (T->isReferenceType()) {
22662329
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
22672330
<< diag::TraitNotSatisfiedReason::Ref;
2331+
return;
2332+
}
2333+
T = T.getNonReferenceType();
2334+
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
2335+
if (!D || D->isInvalidDecl())
2336+
return;
2337+
2338+
if (D->hasDefinition())
2339+
DiagnoseNonStandardLayoutReason(SemaRef, Loc, D);
2340+
2341+
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
2342+
}
2343+
2344+
static bool hasMixedAccessSpecifier(const CXXRecordDecl *D) {
2345+
AccessSpecifier FirstAccess = AS_none;
2346+
for (const FieldDecl *Field : D->fields()) {
2347+
if (Field->isUnnamedBitField())
2348+
continue;
2349+
AccessSpecifier FieldAccess = Field->getAccess();
2350+
if (FirstAccess == AS_none) {
2351+
FirstAccess = FieldAccess;
2352+
} else if (FieldAccess != FirstAccess) {
2353+
return true;
2354+
}
2355+
}
2356+
return false;
2357+
}
22682358

2359+
static bool hasMultipleDataBaseClassesWithFields(const CXXRecordDecl *D) {
2360+
int NumBasesWithFields = 0;
2361+
for (const CXXBaseSpecifier &Base : D->bases()) {
2362+
const CXXRecordDecl *BaseRD = Base.getType()->getAsCXXRecordDecl();
2363+
if (!BaseRD || BaseRD->isInvalidDecl())
2364+
continue;
2365+
2366+
for (const FieldDecl *Field : BaseRD->fields()) {
2367+
if (!Field->isUnnamedBitField()) {
2368+
++NumBasesWithFields;
2369+
break; // Only count the base once.
2370+
}
2371+
}
2372+
}
2373+
return NumBasesWithFields > 1;
2374+
}
2375+
2376+
static void DiagnoseNonStandardLayoutReason(Sema &SemaRef, SourceLocation Loc,
2377+
const CXXRecordDecl *D) {
2378+
for (const CXXBaseSpecifier &B : D->bases()) {
2379+
assert(B.getType()->getAsCXXRecordDecl() && "invalid base?");
2380+
if (B.isVirtual()) {
2381+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2382+
<< diag::TraitNotSatisfiedReason::VBase << B.getType()
2383+
<< B.getSourceRange();
2384+
}
2385+
if (!B.getType()->isStandardLayoutType()) {
2386+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2387+
<< diag::TraitNotSatisfiedReason::NonStdLayoutBase << B.getType()
2388+
<< B.getSourceRange();
2389+
}
2390+
}
2391+
if (hasMixedAccessSpecifier(D)) {
2392+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2393+
<< diag::TraitNotSatisfiedReason::MixedAccess;
2394+
}
2395+
if (hasMultipleDataBaseClassesWithFields(D)) {
2396+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2397+
<< diag::TraitNotSatisfiedReason::MultipleDataBase;
2398+
}
2399+
if (D->isPolymorphic()) {
2400+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2401+
<< diag::TraitNotSatisfiedReason::VirtualFunction;
2402+
}
2403+
for (const FieldDecl *Field : D->fields()) {
2404+
if (!Field->getType()->isStandardLayoutType()) {
2405+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2406+
<< diag::TraitNotSatisfiedReason::NonStdLayoutMember << Field
2407+
<< Field->getType() << Field->getSourceRange();
2408+
}
2409+
}
2410+
}
2411+
2412+
static void DiagnoseNonStandardLayoutReason(Sema &SemaRef, SourceLocation Loc,
2413+
QualType T) {
2414+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
2415+
<< T << diag::TraitName::StandardLayout;
2416+
2417+
// Check type-level exclusion first
2418+
if (T->isVariablyModifiedType()) {
2419+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2420+
<< diag::TraitNotSatisfiedReason::VLA;
2421+
return;
2422+
}
2423+
2424+
if (T->isReferenceType()) {
2425+
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
2426+
<< diag::TraitNotSatisfiedReason::Ref;
2427+
return;
2428+
}
2429+
T = T.getNonReferenceType();
22692430
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
22702431
if (!D || D->isInvalidDecl())
22712432
return;
22722433

22732434
if (D->hasDefinition())
2274-
DiagnoseNonTriviallyCopyableReason(SemaRef, Loc, D);
2435+
DiagnoseNonStandardLayoutReason(SemaRef, Loc, D);
22752436

22762437
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
22772438
}
@@ -2296,6 +2457,12 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
22962457
case UTT_IsTriviallyCopyable:
22972458
DiagnoseNonTriviallyCopyableReason(*this, E->getBeginLoc(), Args[0]);
22982459
break;
2460+
case TT_IsConstructible:
2461+
DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
2462+
break;
2463+
case UTT_IsStandardLayout:
2464+
DiagnoseNonStandardLayoutReason(*this, E->getBeginLoc(), Args[0]);
2465+
break;
22992466
default:
23002467
break;
23012468
}

clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ struct is_trivially_copyable {
2020

2121
template <typename T>
2222
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
23+
24+
template <typename... Args>
25+
struct is_constructible {
26+
static constexpr bool value = __is_constructible(Args...);
27+
};
28+
29+
template <typename... Args>
30+
constexpr bool is_constructible_v = __is_constructible(Args...);
31+
32+
template <typename T>
33+
struct is_standard_layout {
34+
static constexpr bool value = __is_standard_layout(T);
35+
};
36+
template <typename T>
37+
constexpr bool is_standard_layout_v = __is_standard_layout(T);
2338
#endif
2439

2540
#ifdef STD2
@@ -44,6 +59,26 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>;
4459

4560
template <typename T>
4661
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
62+
63+
template <typename... Args>
64+
struct __details_is_constructible{
65+
static constexpr bool value = __is_constructible(Args...);
66+
};
67+
68+
template <typename... Args>
69+
using is_constructible = __details_is_constructible<Args...>;
70+
71+
template <typename... Args>
72+
constexpr bool is_constructible_v = __is_constructible(Args...);
73+
74+
template <typename T>
75+
struct __details_is_standard_layout {
76+
static constexpr bool value = __is_standard_layout(T);
77+
};
78+
template <typename T>
79+
using is_standard_layout = __details_is_standard_layout<T>;
80+
template <typename T>
81+
constexpr bool is_standard_layout_v = __is_standard_layout(T);
4782
#endif
4883

4984

@@ -73,6 +108,24 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>;
73108

74109
template <typename T>
75110
constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
111+
112+
template <typename... Args>
113+
struct __details_is_constructible : bool_constant<__is_constructible(Args...)> {};
114+
115+
template <typename... Args>
116+
using is_constructible = __details_is_constructible<Args...>;
117+
118+
template <typename... Args>
119+
constexpr bool is_constructible_v = is_constructible<Args...>::value;
120+
121+
122+
template <typename T>
123+
struct __details_is_standard_layout : bool_constant<__is_standard_layout(T)> {};
124+
template <typename T>
125+
using is_standard_layout = __details_is_standard_layout<T>;
126+
template <typename T>
127+
constexpr bool is_standard_layout_v = is_standard_layout<T>::value;
128+
76129
#endif
77130

78131
}
@@ -100,6 +153,30 @@ static_assert(std::is_trivially_copyable_v<int&>);
100153
// expected-note@-1 {{because it is a reference type}}
101154

102155

156+
// Direct tests
157+
static_assert(std::is_standard_layout<int>::value);
158+
static_assert(std::is_standard_layout_v<int>);
159+
160+
static_assert(std::is_standard_layout<int&>::value);
161+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_standard_layout<int &>::value'}} \
162+
// expected-note@-1 {{'int &' is not standard-layout}} \
163+
// expected-note@-1 {{because it is a reference type}}
164+
165+
static_assert(std::is_standard_layout_v<int&>);
166+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_standard_layout_v<int &>'}} \
167+
// expected-note@-1 {{'int &' is not standard-layout}} \
168+
// expected-note@-1 {{because it is a reference type}}
169+
170+
171+
static_assert(std::is_constructible<int, int>::value);
172+
173+
static_assert(std::is_constructible<void>::value);
174+
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_constructible<void>::value'}} \
175+
// expected-note@-1 {{because it is a cv void type}}
176+
static_assert(std::is_constructible_v<void>);
177+
// expected-error@-1 {{static assertion failed due to requirement 'std::is_constructible_v<void>'}} \
178+
// expected-note@-1 {{because it is a cv void type}}
179+
103180
namespace test_namespace {
104181
using namespace std;
105182
static_assert(is_trivially_relocatable<int&>::value);
@@ -119,9 +196,28 @@ namespace test_namespace {
119196
// expected-error@-1 {{static assertion failed due to requirement 'is_trivially_copyable_v<int &>'}} \
120197
// expected-note@-1 {{'int &' is not trivially copyable}} \
121198
// expected-note@-1 {{because it is a reference type}}
199+
200+
static_assert(is_constructible<void>::value);
201+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_constructible<void>::value'}} \
202+
// expected-note@-1 {{because it is a cv void type}}
203+
static_assert(is_constructible_v<void>);
204+
// expected-error@-1 {{static assertion failed due to requirement 'is_constructible_v<void>'}} \
205+
// expected-note@-1 {{because it is a cv void type}}
206+
207+
static_assert(is_standard_layout<int&>::value);
208+
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_standard_layout<int &>::value'}} \
209+
// expected-note@-1 {{'int &' is not standard-layout}} \
210+
// expected-note@-1 {{because it is a reference type}}
211+
212+
static_assert(is_standard_layout_v<int&>);
213+
// expected-error@-1 {{static assertion failed due to requirement 'is_standard_layout_v<int &>'}} \
214+
// expected-note@-1 {{'int &' is not standard-layout}} \
215+
// expected-note@-1 {{because it is a reference type}}
216+
122217
}
123218

124219

220+
125221
namespace concepts {
126222
template <typename T>
127223
requires std::is_trivially_relocatable<T>::value void f(); // #cand1
@@ -192,3 +288,5 @@ static_assert(std::is_replaceable_v<int&>);
192288
// expected-error@-1 {{static assertion failed due to requirement 'std::is_replaceable_v<int &>'}} \
193289
// expected-note@-1 {{'int &' is not replaceable}} \
194290
// expected-note@-1 {{because it is a reference type}}
291+
292+

0 commit comments

Comments
 (0)