https://github.com/snarang181 updated 
https://github.com/llvm/llvm-project/pull/143722

>From e96c9c940bbfaf9da752c3537ea8bd023b8b15aa Mon Sep 17 00:00:00 2001
From: Samarth Narang <snar...@umass.edu>
Date: Mon, 2 Jun 2025 19:30:39 -0400
Subject: [PATCH 1/2] Add std layout diagnostics Add diagnostic test cases

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   8 +-
 clang/lib/Sema/SemaTypeTraits.cpp             | 100 ++++++++++++++++++
 .../type-traits-unsatisfied-diags-std.cpp     |  54 ++++++++++
 .../SemaCXX/type-traits-unsatisfied-diags.cpp |  80 ++++++++++++++
 4 files changed, 241 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 95d24e9f1e6b5..dfe68991afb8a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1768,7 +1768,8 @@ def note_unsatisfied_trait
            "%TriviallyRelocatable{trivially relocatable}|"
            "%Replaceable{replaceable}|"
            "%TriviallyCopyable{trivially copyable}|"
-           "%Constructible{constructible with provided types}"
+           "%Constructible{constructible with provided types}|"
+                                  "%StandardLayout{standard-layout}"
            "}1">;
 
 def note_unsatisfied_trait_reason
@@ -1788,6 +1789,11 @@ 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}|"
            "%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
            "%UserProvidedCtr{has a user provided %select{copy|move}1 "
            "constructor}|"
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index 22c690bedc1ed..3134487f7e76d 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1949,6 +1949,7 @@ static std::optional<TypeTrait> 
StdNameToTypeTrait(StringRef Name) {
       .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);
 }
 
@@ -2340,6 +2341,102 @@ static void DiagnoseNonTriviallyCopyableReason(Sema 
&SemaRef,
   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();
+    }
+  }
+}
+
+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())
@@ -2363,6 +2460,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
   case TT_IsConstructible:
     DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
     break;
+  case UTT_IsStandardLayout:
+    DiagnoseNonStandardLayoutReason(*this, E->getBeginLoc(), Args[0]);
+    break;
   default:
     break;
   }
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp 
b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
index a403a0450607a..0aec240672d1c 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -28,6 +28,13 @@ struct is_constructible {
 
 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
@@ -63,6 +70,15 @@ 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
 
 
@@ -101,6 +117,15 @@ 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
 
 }
@@ -128,6 +153,21 @@ 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);
@@ -163,9 +203,21 @@ namespace test_namespace {
     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}}
+    
 }
 
 
+
 namespace concepts {
 template <typename T>
 requires std::is_trivially_relocatable<T>::value void f();  // #cand1
@@ -258,3 +310,5 @@ static_assert(std::is_replaceable_v<int&>);
 // expected-error@-1 {{static assertion failed due to requirement 
'std::is_replaceable_v<int &>'}} \
 // expected-note@-1 {{'int &' is not replaceable}} \
 // expected-note@-1 {{because it is a reference type}}
+
+
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp 
b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index d0b3f294fbcab..d3f9b64fa2bfc 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -550,3 +550,83 @@ static_assert(__is_constructible(void (int, float)));
 // expected-error@-1 {{static assertion failed due to requirement 
'__is_constructible(void (int, float))'}} \
 // expected-note@-1 {{because it is a function type}}
 }
+
+namespace standard_layout_tests {
+    struct WithVirtual { // #sl-Virtual
+  virtual void foo();
+};
+static_assert(__is_standard_layout(WithVirtual));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_standard_layout(standard_layout_tests::WithVirtual)'}} \
+// expected-note@-1 {{'WithVirtual' is not standard-layout}} \
+// expected-note@-1 {{because it has virtual functions}} \
+// expected-note@#sl-Virtual {{'WithVirtual' defined here}}
+
+struct MixedAccess { // #sl-Mixed
+public:
+  int a;
+private:
+  int b;
+};
+static_assert(__is_standard_layout(MixedAccess));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_standard_layout(standard_layout_tests::MixedAccess)'}} \
+// expected-note@-1 {{'MixedAccess' is not standard-layout}} \
+// expected-note@-1 {{because it has mixed access specifiers}} \
+// expected-note@#sl-Mixed {{'MixedAccess' defined here}}
+
+struct VirtualBase { virtual ~VirtualBase(); };               // 
#sl-VirtualBase
+struct VB : virtual VirtualBase {};                            // #sl-VB
+static_assert(__is_standard_layout(VB));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_standard_layout(standard_layout_tests::VB)'}} \
+// expected-note@-1 {{'VB' is not standard-layout}} \
+// expected-note@-1 {{because it has a virtual base 'VirtualBase'}} \
+// expected-note@-1 {{because it has a non-standard-layout base 
'VirtualBase'}} \
+// expected-note@-1 {{because it has virtual functions}}
+// expected-note@#sl-VB {{'VB' defined here}}
+
+union U {      // #sl-U
+public:
+    int x;
+private:
+    int y;
+};                                                       
+static_assert(__is_standard_layout(U));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_standard_layout(standard_layout_tests::U)'}} \
+// expected-note@-1 {{'U' is not standard-layout}} \
+// expected-note@-1 {{because it has mixed access specifiers}}
+// expected-note@#sl-U {{'U' defined here}}
+
+// Single base class is OK
+struct BaseClass{ int a; };                                   // #sl-BaseClass
+struct DerivedOK : BaseClass {};                                // 
#sl-DerivedOK
+static_assert(__is_standard_layout(DerivedOK));    
+
+// Primitive types should be standard layout
+static_assert(__is_standard_layout(int));                     // #sl-Int
+static_assert(__is_standard_layout(float));                   // #sl-Float
+
+// Multi-level inheritance: Non-standard layout
+struct Base1 { int a; };                                      // #sl-Base1
+struct Base2 { int b; };                                      // #sl-Base2
+struct DerivedClass : Base1, Base2 {};                        // 
#sl-DerivedClass
+static_assert(__is_standard_layout(DerivedClass));               
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_standard_layout(standard_layout_tests::DerivedClass)'}} \
+// expected-note@-1 {{'DerivedClass' is not standard-layout}} \
+// expected-note@-1 {{because it has multiple base classes with data members}} 
\
+// expected-note@#sl-DerivedClass {{'DerivedClass' defined here}} 
+
+// Inheritance hierarchy with multiple classes having data members
+struct BaseA { int a; };                                      // #sl-BaseA
+struct BaseB : BaseA {};                                      // inherits 
BaseA, has no new members
+struct BaseC: BaseB { int c; };                               // #sl-BaseC
+static_assert(__is_standard_layout(BaseC));
+// expected-error@-1 {{static assertion failed due to requirement 
'__is_standard_layout(standard_layout_tests::BaseC)'}} \
+// expected-note@-1 {{'BaseC' is not standard-layout}} \
+// expected-note@#sl-BaseC {{'BaseC' defined here}} \
+
+// Multiple direct base classes with no data members --> standard layout
+struct BaseX {};                                              // #sl-BaseX
+struct BaseY {};                                              // #sl-BaseY
+struct MultiBase : BaseX, BaseY {};                          // #sl-MultiBase
+static_assert(__is_standard_layout(MultiBase));
+
+}

>From 0754b80e3c20eb6e5c550bb805c541ab7fdef936 Mon Sep 17 00:00:00 2001
From: Samarth Narang <snar...@umass.edu>
Date: Thu, 12 Jun 2025 22:48:48 -0400
Subject: [PATCH 2/2] Add more descriptive messages for indirect base class
 inheritance

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  1 +
 clang/lib/Sema/SemaTypeTraits.cpp             | 31 +++++++++++++++++++
 .../SemaCXX/type-traits-unsatisfied-diags.cpp |  1 +
 3 files changed, 33 insertions(+)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index dfe68991afb8a..9d4695763b832 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1794,6 +1794,7 @@ def note_unsatisfied_trait_reason
            "%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}|"
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index 3134487f7e76d..9b5c7e0f91dd6 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -2407,6 +2407,37 @@ static void DiagnoseNonStandardLayoutReason(Sema 
&SemaRef, SourceLocation Loc,
           << 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,
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp 
b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index d3f9b64fa2bfc..9a934187a13ee 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -621,6 +621,7 @@ struct BaseC: BaseB { int c; };                             
  // #sl-BaseC
 static_assert(__is_standard_layout(BaseC));
 // expected-error@-1 {{static assertion failed due to requirement 
'__is_standard_layout(standard_layout_tests::BaseC)'}} \
 // expected-note@-1 {{'BaseC' is not standard-layout}} \
+// expected-note@-1 {{because it has an indirect base 'BaseA' with data 
members}} \
 // expected-note@#sl-BaseC {{'BaseC' defined here}} \
 
 // Multiple direct base classes with no data members --> standard layout

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to