https://github.com/ClaytonKnittel created 
https://github.com/llvm/llvm-project/pull/221615

[[class.mem]](https://timsong-cpp.github.io/cppwp/n3337/class.mem#19) states:

> "If a standard-layout union contains two or more standard-layout structs that 
> share a common initial sequence, and if the standard-layout union object 
> currently contains one of these standard-layout structs, it is permitted to 
> inspect the common initial part of any of them. Two standard-layout structs 
> share a common initial sequence if corresponding members have 
> layout-compatible types and either neither member is a bit-field or both are 
> bit-fields with the same width for a sequence of one or more initial members."

This makes it possible to obtain a reference to a type which was never 
constructed, which violates the assumption made by constructor homing that all 
types that may require debug info must be constructed.

This change takes the conservative approach of always emitting full debug info 
for standard-layout types which appear as a member of a standard-layout union 
somewhere in the TU. It could be made more aggressive by only emitting full 
debug info if there is another type in the union that shares a "common initial 
sequence", but that would add complexity to this exclusion logic and likely 
isn't worth it.

>From 59ac3df612c1b38256d472c4358eca18700f6999 Mon Sep 17 00:00:00 2001
From: Clayton Knittel <[email protected]>
Date: Sun, 6 Sep 2026 23:45:05 +0000
Subject: [PATCH] Always emit complete debug info for types which appear as
 members of a standard-layout union.

[[class.mem]](https://timsong-cpp.github.io/cppwp/n3337/class.mem#19)
states:

> "If a standard-layout union contains two or more standard-layout structs that 
> share a common initial sequence, and if the standard-layout union object 
> currently contains one of these standard-layout structs, it is permitted to 
> inspect the common initial part of any of them. Two standard-layout structs 
> share a common initial sequence if corresponding members have 
> layout-compatible types and either neither member is a bit-field or both are 
> bit-fields with the same width for a sequence of one or more initial members."

This makes it possible to obtain a reference to a type which was never
constructed, which violates the assumption made by constructor homing
that all types that may require debug info must be constructed.

This change takes the conservative approach of always emitting full
debug info for standard-layout types which appear as a member of a
standard-layout union somewhere in the TU. It could be made more
aggressive by only emitting full debug info if there is another type in
the union that shares a "common initial sequence", but that would add
complexity to this exclusion logic and likely isn't worth it.

Signed-off-by: Clayton Knittel <[email protected]>
---
 .../clang/AST/CXXRecordDeclDefinitionBits.def |   3 +
 clang/include/clang/AST/DeclCXX.h             | 348 ++++++++----------
 clang/lib/AST/DeclCXX.cpp                     |  16 +-
 clang/lib/CodeGen/CGDebugInfo.cpp             |   3 +-
 clang/test/DebugInfo/CXX/limited-ctor.cpp     |  20 +-
 clang/unittests/AST/DeclTest.cpp              | 256 +++++++++++++
 6 files changed, 448 insertions(+), 198 deletions(-)

diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def 
b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
index 97e61aaec7d51..a45a57ecfff20 100644
--- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
+++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
@@ -64,6 +64,9 @@ FIELD(Abstract, 1, NO_MERGE)
 /// language rules (including DRs).
 FIELD(IsStandardLayout, 1, NO_MERGE)
 
+/// True when this class appears as a member of some standard-layout union.
+FIELD(IsStandardLayoutUnionMember, 1, MERGE_OR)
+
 /// True when this class was standard-layout under the C++11
 /// definition.
 ///
diff --git a/clang/include/clang/AST/DeclCXX.h 
b/clang/include/clang/AST/DeclCXX.h
index afe46fae1bceb..fe05f8e122923 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -87,9 +87,9 @@ class AccessSpecDecl : public Decl {
   /// The location of the ':'.
   SourceLocation ColonLoc;
 
-  AccessSpecDecl(AccessSpecifier AS, DeclContext *DC,
-                 SourceLocation ASLoc, SourceLocation ColonLoc)
-    : Decl(AccessSpec, DC, ASLoc), ColonLoc(ColonLoc) {
+  AccessSpecDecl(AccessSpecifier AS, DeclContext *DC, SourceLocation ASLoc,
+                 SourceLocation ColonLoc)
+      : Decl(AccessSpec, DC, ASLoc), ColonLoc(ColonLoc) {
     setAccess(AS);
   }
 
@@ -186,8 +186,8 @@ class CXXBaseSpecifier {
   CXXBaseSpecifier() = default;
   CXXBaseSpecifier(SourceRange R, bool V, bool BC, AccessSpecifier A,
                    TypeSourceInfo *TInfo, SourceLocation EllipsisLoc)
-    : Range(R), EllipsisLoc(EllipsisLoc), Virtual(V), BaseOfClass(BC),
-      Access(A), InheritConstructors(false), BaseTypeInfo(TInfo) {}
+      : Range(R), EllipsisLoc(EllipsisLoc), Virtual(V), BaseOfClass(BC),
+        Access(A), InheritConstructors(false), BaseTypeInfo(TInfo) {}
 
   /// Retrieves the source range that contains the entire base specifier.
   SourceRange getSourceRange() const LLVM_READONLY { return Range; }
@@ -218,9 +218,7 @@ class CXXBaseSpecifier {
   }
 
   /// For a pack expansion, determine the location of the ellipsis.
-  SourceLocation getEllipsisLoc() const {
-    return EllipsisLoc;
-  }
+  SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
 
   /// Returns the access specifier for this base specifier.
   ///
@@ -229,7 +227,7 @@ class CXXBaseSpecifier {
   /// written in the source code, use getAccessSpecifierAsWritten().
   AccessSpecifier getAccessSpecifier() const {
     if ((AccessSpecifier)Access == AS_none)
-      return BaseOfClass? AS_private : AS_public;
+      return BaseOfClass ? AS_private : AS_public;
     else
       return (AccessSpecifier)Access;
   }
@@ -290,9 +288,8 @@ class CXXRecordDecl : public RecordDecl {
 
 private:
   struct DefinitionData {
-    #define FIELD(Name, Width, Merge) \
-    unsigned Name : Width;
-    #include "CXXRecordDeclDefinitionBits.def"
+#define FIELD(Name, Width, Merge) unsigned Name : Width;
+#include "CXXRecordDeclDefinitionBits.def"
 
     /// Whether this class describes a C++ lambda.
     LLVM_PREFERRED_TYPE(bool)
@@ -429,7 +426,7 @@ class CXXRecordDecl : public RecordDecl {
     /// lambda. One list is provided for each merged copy of the lambda.
     /// The first list corresponds to the canonical definition.
     /// The destructor is registered by AddCaptureList when necessary.
-    llvm::TinyPtrVector<Capture*> Captures;
+    llvm::TinyPtrVector<Capture *> Captures;
 
     /// The type of the call method.
     TypeSourceInfo *MethodTyInfo;
@@ -469,7 +466,7 @@ class CXXRecordDecl : public RecordDecl {
     // properties.
     auto *DD = DefinitionData;
     assert(DD && DD->IsLambda && "queried lambda property of non-lambda 
class");
-    return static_cast<LambdaDefinitionData&>(*DD);
+    return static_cast<LambdaDefinitionData &>(*DD);
   }
 
   /// The template or declaration that this declaration
@@ -524,25 +521,25 @@ class CXXRecordDecl : public RecordDecl {
   }
 
   const CXXRecordDecl *getCanonicalDecl() const {
-    return const_cast<CXXRecordDecl*>(this)->getCanonicalDecl();
+    return const_cast<CXXRecordDecl *>(this)->getCanonicalDecl();
   }
 
   CXXRecordDecl *getPreviousDecl() {
     return cast_or_null<CXXRecordDecl>(
-            static_cast<RecordDecl *>(this)->getPreviousDecl());
+        static_cast<RecordDecl *>(this)->getPreviousDecl());
   }
 
   const CXXRecordDecl *getPreviousDecl() const {
-    return const_cast<CXXRecordDecl*>(this)->getPreviousDecl();
+    return const_cast<CXXRecordDecl *>(this)->getPreviousDecl();
   }
 
   CXXRecordDecl *getMostRecentDecl() {
     return cast<CXXRecordDecl>(
-            static_cast<RecordDecl *>(this)->getMostRecentDecl());
+        static_cast<RecordDecl *>(this)->getMostRecentDecl());
   }
 
   const CXXRecordDecl *getMostRecentDecl() const {
-    return const_cast<CXXRecordDecl*>(this)->getMostRecentDecl();
+    return const_cast<CXXRecordDecl *>(this)->getMostRecentDecl();
   }
 
   CXXRecordDecl *getDefinition() const {
@@ -596,7 +593,7 @@ class CXXRecordDecl : public RecordDecl {
   unsigned getODRHash() const;
 
   /// Sets the base classes of this struct or class.
-  void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases);
+  void setBases(CXXBaseSpecifier const *const *Bases, unsigned NumBases);
 
   /// Retrieves the number of base classes of this class.
   unsigned getNumBases() const { return data().NumBases; }
@@ -658,9 +655,7 @@ class CXXRecordDecl : public RecordDecl {
   }
 
   /// Method past-the-end iterator.
-  method_iterator method_end() const {
-    return method_iterator(decls_end());
-  }
+  method_iterator method_end() const { return method_iterator(decls_end()); }
 
   /// Iterator access to constructor members.
   using ctor_iterator = specific_decl_iterator<CXXConstructorDecl>;
@@ -669,13 +664,9 @@ class CXXRecordDecl : public RecordDecl {
 
   ctor_range ctors() const { return ctor_range(ctor_begin(), ctor_end()); }
 
-  ctor_iterator ctor_begin() const {
-    return ctor_iterator(decls_begin());
-  }
+  ctor_iterator ctor_begin() const { return ctor_iterator(decls_begin()); }
 
-  ctor_iterator ctor_end() const {
-    return ctor_iterator(decls_end());
-  }
+  ctor_iterator ctor_end() const { return ctor_iterator(decls_end()); }
 
   /// An iterator over friend declarations.  All of these are defined
   /// in DeclFriend.h.
@@ -688,9 +679,7 @@ class CXXRecordDecl : public RecordDecl {
   void pushFriendDecl(FriendDecl *FD);
 
   /// Determines whether this record has any friends.
-  bool hasFriends() const {
-    return data().FirstFriend.isValid();
-  }
+  bool hasFriends() const { return data().FirstFriend.isValid(); }
 
   bool hasLazyFriends() const { return data().FirstFriend.isOffset(); }
 
@@ -753,8 +742,7 @@ class CXXRecordDecl : public RecordDecl {
   /// \c true if we know for sure that this class has an accessible
   /// destructor that is not deleted.
   bool hasSimpleDestructor() const {
-    return !hasUserDeclaredDestructor() &&
-           !data().DefaultedDestructorIsDeleted;
+    return !hasUserDeclaredDestructor() && 
!data().DefaultedDestructorIsDeleted;
   }
 
   /// Determine whether this class has any default constructors.
@@ -767,16 +755,16 @@ class CXXRecordDecl : public RecordDecl {
   /// this class.
   ///
   /// This value is used for lazy creation of default constructors.
-    bool needsImplicitDefaultConstructor() const {
+  bool needsImplicitDefaultConstructor() const {
     return (!getLangOpts().HLSL || isHLSLBuiltinRecord()) &&
            ((!data().UserDeclaredConstructor &&
-            !(data().DeclaredSpecialMembers & SMF_DefaultConstructor) &&
-            (!isLambda() || lambdaIsDefaultConstructibleAndAssignable())) ||
-           // FIXME: Proposed fix to core wording issue: if a class inherits
-           // a default constructor and doesn't explicitly declare one, one
-           // is declared implicitly.
-           (data().HasInheritedDefaultConstructor &&
-            !(data().DeclaredSpecialMembers & SMF_DefaultConstructor)));
+             !(data().DeclaredSpecialMembers & SMF_DefaultConstructor) &&
+             (!isLambda() || lambdaIsDefaultConstructibleAndAssignable())) ||
+            // FIXME: Proposed fix to core wording issue: if a class inherits
+            // a default constructor and doesn't explicitly declare one, one
+            // is declared implicitly.
+            (data().HasInheritedDefaultConstructor &&
+             !(data().DeclaredSpecialMembers & SMF_DefaultConstructor)));
   }
 
   /// Determine whether this class has any user-declared constructors.
@@ -844,7 +832,7 @@ class CXXRecordDecl : public RecordDecl {
   /// implicitly declared.
   bool hasUserDeclaredMoveOperation() const {
     return data().UserDeclaredSpecialMembers &
-             (SMF_MoveConstructor | SMF_MoveAssignment);
+           (SMF_MoveConstructor | SMF_MoveAssignment);
   }
 
   /// Determine whether this class has had a move constructor
@@ -900,8 +888,7 @@ class CXXRecordDecl : public RecordDecl {
            (!getLangOpts().HLSL || isHLSLBuiltinRecord()) &&
            !hasUserDeclaredCopyConstructor() &&
            !hasUserDeclaredCopyAssignment() &&
-           !hasUserDeclaredMoveAssignment() &&
-           !hasUserDeclaredDestructor();
+           !hasUserDeclaredMoveAssignment() && !hasUserDeclaredDestructor();
   }
 
   /// Determine whether we need to eagerly declare a defaulted move
@@ -993,8 +980,7 @@ class CXXRecordDecl : public RecordDecl {
            (!getLangOpts().HLSL || isHLSLBuiltinRecord()) &&
            !hasUserDeclaredCopyConstructor() &&
            !hasUserDeclaredCopyAssignment() &&
-           !hasUserDeclaredMoveConstructor() &&
-           !hasUserDeclaredDestructor() &&
+           !hasUserDeclaredMoveConstructor() && !hasUserDeclaredDestructor() &&
            (!isLambda() || lambdaIsDefaultConstructibleAndAssignable());
   }
 
@@ -1108,7 +1094,8 @@ class CXXRecordDecl : public RecordDecl {
   }
 
   capture_const_iterator captures_begin() const {
-    if (!isLambda()) return nullptr;
+    if (!isLambda())
+      return nullptr;
     LambdaDefinitionData &LambdaData = getLambdaData();
     return LambdaData.Captures.empty() ? nullptr : LambdaData.Captures.front();
   }
@@ -1197,13 +1184,9 @@ class CXXRecordDecl : public RecordDecl {
   void setInitMethod(bool Val) { data().HasInitMethod = Val; }
   bool hasInitMethod() const { return data().HasInitMethod; }
 
-  bool hasPrivateFields() const {
-    return data().HasPrivateFields;
-  }
+  bool hasPrivateFields() const { return data().HasPrivateFields; }
 
-  bool hasProtectedFields() const {
-    return data().HasProtectedFields;
-  }
+  bool hasProtectedFields() const { return data().HasProtectedFields; }
 
   /// Determine whether this class has direct non-static data members.
   bool hasDirectFields() const {
@@ -1233,6 +1216,15 @@ class CXXRecordDecl : public RecordDecl {
   /// C++ [class]p7.
   bool isStandardLayout() const { return data().IsStandardLayout; }
 
+  /// Determine whether this class appears as a member of a standard-layout
+  /// union.
+  bool isStandardLayoutUnionMember() const {
+    return data().IsStandardLayoutUnionMember;
+  }
+  void setIsStandardLayoutUnionMember(bool V = true) {
+    data().IsStandardLayoutUnionMember = V;
+  }
+
   /// Determine whether this class was standard-layout per
   /// C++11 [class]p7, specifically using the C++11 rules without any DRs.
   bool isCXX11StandardLayout() const { return data().IsCXX11StandardLayout; }
@@ -1369,8 +1361,7 @@ class CXXRecordDecl : public RecordDecl {
   /// Determine whether a defaulted default constructor for this class
   /// would be constexpr.
   bool defaultedDestructorIsConstexpr() const {
-    return data().DefaultedDestructorIsConstexpr &&
-           getLangOpts().CPlusPlus20;
+    return data().DefaultedDestructorIsConstexpr && getLangOpts().CPlusPlus20;
   }
 
   /// Determine whether this class has a constexpr destructor.
@@ -1432,9 +1423,7 @@ class CXXRecordDecl : public RecordDecl {
 
   /// Determine whether this class has a using-declaration that names
   /// a base class assignment operator.
-  bool hasInheritedAssignment() const {
-    return data().HasInheritedAssignment;
-  }
+  bool hasInheritedAssignment() const { return data().HasInheritedAssignment; }
 
   /// Determine whether this class is considered trivially copyable per
   /// (C++11 [class]p6).
@@ -1484,8 +1473,10 @@ class CXXRecordDecl : public RecordDecl {
   void addedSelectedDestructor(CXXDestructorDecl *DD);
 
   /// Notify the class that an eligible SMF has been added.
-  /// This updates triviality and destructor based properties of the class 
accordingly.
-  void addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD, unsigned 
SMKind);
+  /// This updates triviality and destructor based properties of the class
+  /// accordingly.
+  void addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
+                                          unsigned SMKind);
 
   /// If this record is an instantiation of a member class,
   /// retrieves the member class from which it was instantiated.
@@ -1558,7 +1549,9 @@ class CXXRecordDecl : public RecordDecl {
 
   /// Returns true if the class destructor, or any implicitly invoked
   /// destructors are marked noreturn.
-  bool isAnyDestructorNoReturn() const { return 
data().IsAnyDestructorNoReturn; }
+  bool isAnyDestructorNoReturn() const {
+    return data().IsAnyDestructorNoReturn;
+  }
 
   /// Returns true if the class contains HLSL intangible type, either as
   /// a field or in base class.
@@ -1582,8 +1575,8 @@ class CXXRecordDecl : public RecordDecl {
   }
 
   FunctionDecl *isLocalClass() {
-    return const_cast<FunctionDecl*>(
-        const_cast<const CXXRecordDecl*>(this)->isLocalClass());
+    return const_cast<FunctionDecl *>(
+        const_cast<const CXXRecordDecl *>(this)->isLocalClass());
   }
 
   /// Determine whether this dependent class is a current instantiation,
@@ -1667,9 +1660,8 @@ class CXXRecordDecl : public RecordDecl {
   /// base named by the \p Specifier.
   ///
   /// \returns true if this base matched the search criteria, false otherwise.
-  using BaseMatchesCallback =
-      llvm::function_ref<bool(const CXXBaseSpecifier *Specifier,
-                              CXXBasePath &Path)>;
+  using BaseMatchesCallback = llvm::function_ref<bool(
+      const CXXBaseSpecifier *Specifier, CXXBasePath &Path)>;
 
   /// Look for entities within the base classes of this C++ class,
   /// transitively searching all base class subobjects.
@@ -1724,7 +1716,7 @@ class CXXRecordDecl : public RecordDecl {
   void getFinalOverriders(CXXFinalOverriderMap &FinaOverriders) const;
 
   /// Get the indirect primary bases for this class.
-  void getIndirectPrimaryBases(CXXIndirectPrimaryBaseSet& Bases) const;
+  void getIndirectPrimaryBases(CXXIndirectPrimaryBaseSet &Bases) const;
 
   /// Determine whether this class has a member with the given name, possibly
   /// in a non-dependent base class.
@@ -1737,14 +1729,15 @@ class CXXRecordDecl : public RecordDecl {
   /// Renders and displays an inheritance diagram
   /// for this C++ class and all of its base classes (transitively) using
   /// GraphViz.
-  void viewInheritance(ASTContext& Context) const;
+  void viewInheritance(ASTContext &Context) const;
 
   /// Calculates the access of a decl that is reached
   /// along a path.
   static AccessSpecifier MergeAccess(AccessSpecifier PathAccess,
                                      AccessSpecifier DeclAccess) {
     assert(DeclAccess != AS_none);
-    if (DeclAccess == AS_private) return AS_none;
+    if (DeclAccess == AS_private)
+      return AS_none;
     return (PathAccess > DeclAccess ? PathAccess : DeclAccess);
   }
 
@@ -2199,10 +2192,12 @@ class CXXMethodDecl : public FunctionDecl {
   }
 
   bool isConst() const { return getType()->castAs<FunctionType>()->isConst(); }
-  bool isVolatile() const { return 
getType()->castAs<FunctionType>()->isVolatile(); }
+  bool isVolatile() const {
+    return getType()->castAs<FunctionType>()->isVolatile();
+  }
 
   bool isVirtual() const {
-    CXXMethodDecl *CD = const_cast<CXXMethodDecl*>(this)->getCanonicalDecl();
+    CXXMethodDecl *CD = const_cast<CXXMethodDecl *>(this)->getCanonicalDecl();
 
     // Member function is virtual if it is marked explicitly so, or if it is
     // declared in __interface -- then it is automatically pure virtual.
@@ -2263,15 +2258,15 @@ class CXXMethodDecl : public FunctionDecl {
     return cast<CXXMethodDecl>(FunctionDecl::getCanonicalDecl());
   }
   const CXXMethodDecl *getCanonicalDecl() const {
-    return const_cast<CXXMethodDecl*>(this)->getCanonicalDecl();
+    return const_cast<CXXMethodDecl *>(this)->getCanonicalDecl();
   }
 
   CXXMethodDecl *getMostRecentDecl() {
     return cast<CXXMethodDecl>(
-            static_cast<FunctionDecl *>(this)->getMostRecentDecl());
+        static_cast<FunctionDecl *>(this)->getMostRecentDecl());
   }
   const CXXMethodDecl *getMostRecentDecl() const {
-    return const_cast<CXXMethodDecl*>(this)->getMostRecentDecl();
+    return const_cast<CXXMethodDecl *>(this)->getMostRecentDecl();
   }
 
   void addOverriddenMethod(const CXXMethodDecl *MD);
@@ -2297,7 +2292,7 @@ class CXXMethodDecl : public FunctionDecl {
   /// is the class in which this method is defined.
   CXXRecordDecl *getParent() {
     return const_cast<CXXRecordDecl *>(
-             cast<CXXRecordDecl>(FunctionDecl::getParent()));
+        cast<CXXRecordDecl>(FunctionDecl::getParent()));
   }
 
   /// Return the type of the \c this pointer.
@@ -2359,15 +2354,14 @@ class CXXMethodDecl : public FunctionDecl {
   /// Find if \p RD or one of the classes it inherits from override this 
method.
   /// If so, return it. \p RD is assumed to be a subclass of the class defining
   /// this method (or be the class itself), unless \p MayBeBase is set to true.
-  CXXMethodDecl *
-  getCorrespondingMethodInClass(const CXXRecordDecl *RD,
-                                bool MayBeBase = false);
+  CXXMethodDecl *getCorrespondingMethodInClass(const CXXRecordDecl *RD,
+                                               bool MayBeBase = false);
 
   const CXXMethodDecl *
   getCorrespondingMethodInClass(const CXXRecordDecl *RD,
                                 bool MayBeBase = false) const {
-    return const_cast<CXXMethodDecl *>(this)
-              ->getCorrespondingMethodInClass(RD, MayBeBase);
+    return const_cast<CXXMethodDecl *>(this)->getCorrespondingMethodInClass(
+        RD, MayBeBase);
   }
 
   /// Find if \p RD declares a function that overrides this function, and if 
so,
@@ -2449,27 +2443,23 @@ class CXXCtorInitializer final {
 
 public:
   /// Creates a new base-class initializer.
-  explicit
-  CXXCtorInitializer(ASTContext &Context, TypeSourceInfo *TInfo, bool 
IsVirtual,
-                     SourceLocation L, Expr *Init, SourceLocation R,
-                     SourceLocation EllipsisLoc);
+  explicit CXXCtorInitializer(ASTContext &Context, TypeSourceInfo *TInfo,
+                              bool IsVirtual, SourceLocation L, Expr *Init,
+                              SourceLocation R, SourceLocation EllipsisLoc);
 
   /// Creates a new member initializer.
-  explicit
-  CXXCtorInitializer(ASTContext &Context, FieldDecl *Member,
-                     SourceLocation MemberLoc, SourceLocation L, Expr *Init,
-                     SourceLocation R);
+  explicit CXXCtorInitializer(ASTContext &Context, FieldDecl *Member,
+                              SourceLocation MemberLoc, SourceLocation L,
+                              Expr *Init, SourceLocation R);
 
   /// Creates a new anonymous field initializer.
-  explicit
-  CXXCtorInitializer(ASTContext &Context, IndirectFieldDecl *Member,
-                     SourceLocation MemberLoc, SourceLocation L, Expr *Init,
-                     SourceLocation R);
+  explicit CXXCtorInitializer(ASTContext &Context, IndirectFieldDecl *Member,
+                              SourceLocation MemberLoc, SourceLocation L,
+                              Expr *Init, SourceLocation R);
 
   /// Creates a new delegating initializer.
-  explicit
-  CXXCtorInitializer(ASTContext &Context, TypeSourceInfo *TInfo,
-                     SourceLocation L, Expr *Init, SourceLocation R);
+  explicit CXXCtorInitializer(ASTContext &Context, TypeSourceInfo *TInfo,
+                              SourceLocation L, Expr *Init, SourceLocation R);
 
   /// \return Unique reproducible object identifier.
   int64_t getID(const ASTContext &Context) const;
@@ -2563,9 +2553,7 @@ class CXXCtorInitializer final {
     return nullptr;
   }
 
-  SourceLocation getMemberLocation() const {
-    return MemberOrEllipsisLocation;
-  }
+  SourceLocation getMemberLocation() const { return MemberOrEllipsisLocation; }
 
   /// Determine the source location of the initializer.
   SourceLocation getSourceLocation() const;
@@ -2591,12 +2579,10 @@ class CXXCtorInitializer final {
   /// This assumes that the initializer was written in the source code, and
   /// ensures that isWritten() returns true.
   void setSourceOrder(int Pos) {
-    assert(!IsWritten &&
-           "setSourceOrder() used on implicit initializer");
+    assert(!IsWritten && "setSourceOrder() used on implicit initializer");
     assert(SourceOrder == 0 &&
            "calling twice setSourceOrder() on the same initializer");
-    assert(Pos >= 0 &&
-           "setSourceOrder() used to make an initializer implicit");
+    assert(Pos >= 0 && "setSourceOrder() used to make an initializer 
implicit");
     IsWritten = true;
     SourceOrder = static_cast<unsigned>(Pos);
   }
@@ -2664,10 +2650,9 @@ class CXXConstructorDecl final
   ExplicitSpecifier getExplicitSpecifierInternal() const {
     if (CXXConstructorDeclBits.HasTrailingExplicitSpecifier)
       return *getTrailingObjects<ExplicitSpecifier>();
-    return ExplicitSpecifier(
-        nullptr, CXXConstructorDeclBits.IsSimpleExplicit
-                     ? ExplicitSpecKind::ResolvedTrue
-                     : ExplicitSpecKind::ResolvedFalse);
+    return ExplicitSpecifier(nullptr, CXXConstructorDeclBits.IsSimpleExplicit
+                                          ? ExplicitSpecKind::ResolvedTrue
+                                          : ExplicitSpecKind::ResolvedFalse);
   }
 
   enum TrailingAllocKind {
@@ -2741,9 +2726,7 @@ class CXXConstructorDecl final
   init_const_iterator init_begin() const;
 
   /// Retrieve an iterator past the last initializer.
-  init_iterator       init_end()       {
-    return init_begin() + getNumCtorInitializers();
-  }
+  init_iterator init_end() { return init_begin() + getNumCtorInitializers(); }
 
   /// Retrieve an iterator past the last initializer.
   init_const_iterator init_end() const {
@@ -2771,7 +2754,7 @@ class CXXConstructorDecl final
   /// Determine the number of arguments used to initialize the member
   /// or base.
   unsigned getNumCtorInitializers() const {
-      return CXXConstructorDeclBits.NumCtorInitializers;
+    return CXXConstructorDeclBits.NumCtorInitializers;
   }
 
   void setNumCtorInitializers(unsigned numCtorInitializers) {
@@ -2779,8 +2762,8 @@ class CXXConstructorDecl final
     // This assert added because NumCtorInitializers is stored
     // in CXXConstructorDeclBits as a bitfield and its width has
     // been shrunk from 32 bits to fit into CXXConstructorDeclBitfields.
-    assert(CXXConstructorDeclBits.NumCtorInitializers ==
-           numCtorInitializers && "NumCtorInitializers overflow!");
+    assert(CXXConstructorDeclBits.NumCtorInitializers == numCtorInitializers &&
+           "NumCtorInitializers overflow!");
   }
 
   void setCtorInitializers(CXXCtorInitializer **Initializers) {
@@ -2874,15 +2857,16 @@ class CXXConstructorDecl final
 
   /// Get the constructor that this inheriting constructor is based on.
   InheritedConstructor getInheritedConstructor() const {
-    return isInheritingConstructor() ?
-      *getTrailingObjects<InheritedConstructor>() : InheritedConstructor();
+    return isInheritingConstructor()
+               ? *getTrailingObjects<InheritedConstructor>()
+               : InheritedConstructor();
   }
 
   CXXConstructorDecl *getCanonicalDecl() override {
     return cast<CXXConstructorDecl>(FunctionDecl::getCanonicalDecl());
   }
   const CXXConstructorDecl *getCanonicalDecl() const {
-    return const_cast<CXXConstructorDecl*>(this)->getCanonicalDecl();
+    return const_cast<CXXConstructorDecl *>(this)->getCanonicalDecl();
   }
 
   ArrayRef<CXXDefaultArgExpr *> getCtorClosureDefaultArgs() const;
@@ -2955,7 +2939,7 @@ class CXXDestructorDecl : public CXXMethodDecl {
     return cast<CXXDestructorDecl>(FunctionDecl::getCanonicalDecl());
   }
   const CXXDestructorDecl *getCanonicalDecl() const {
-    return const_cast<CXXDestructorDecl*>(this)->getCanonicalDecl();
+    return const_cast<CXXDestructorDecl *>(this)->getCanonicalDecl();
   }
 
   // Implement isa/cast/dyncast/etc.
@@ -3021,7 +3005,7 @@ class CXXConversionDecl : public CXXMethodDecl {
     return cast<CXXConversionDecl>(FunctionDecl::getCanonicalDecl());
   }
   const CXXConversionDecl *getCanonicalDecl() const {
-    return const_cast<CXXConversionDecl*>(this)->getCanonicalDecl();
+    return const_cast<CXXConversionDecl *>(this)->getCanonicalDecl();
   }
 
   // Implement isa/cast/dyncast/etc.
@@ -3104,11 +3088,11 @@ class LinkageSpecDecl : public Decl, public DeclContext 
{
   static bool classofKind(Kind K) { return K == LinkageSpec; }
 
   static DeclContext *castToDeclContext(const LinkageSpecDecl *D) {
-    return static_cast<DeclContext *>(const_cast<LinkageSpecDecl*>(D));
+    return static_cast<DeclContext *>(const_cast<LinkageSpecDecl *>(D));
   }
 
   static LinkageSpecDecl *castFromDeclContext(const DeclContext *DC) {
-    return static_cast<LinkageSpecDecl *>(const_cast<DeclContext*>(DC));
+    return static_cast<LinkageSpecDecl *>(const_cast<DeclContext *>(DC));
   }
 };
 
@@ -3142,8 +3126,7 @@ class UsingDirectiveDecl : public NamedDecl {
   UsingDirectiveDecl(DeclContext *DC, SourceLocation UsingLoc,
                      SourceLocation NamespcLoc,
                      NestedNameSpecifierLoc QualifierLoc,
-                     SourceLocation IdentLoc,
-                     NamedDecl *Nominated,
+                     SourceLocation IdentLoc, NamedDecl *Nominated,
                      DeclContext *CommonAncestor)
       : NamedDecl(UsingDirective, DC, IdentLoc, getName()), UsingLoc(UsingLoc),
         NamespaceLoc(NamespcLoc), QualifierLoc(QualifierLoc),
@@ -3184,7 +3167,7 @@ class UsingDirectiveDecl : public NamedDecl {
   NamespaceDecl *getNominatedNamespace();
 
   const NamespaceDecl *getNominatedNamespace() const {
-    return const_cast<UsingDirectiveDecl*>(this)->getNominatedNamespace();
+    return const_cast<UsingDirectiveDecl *>(this)->getNominatedNamespace();
   }
 
   /// Returns the common ancestor context of this using-directive and
@@ -3202,13 +3185,11 @@ class UsingDirectiveDecl : public NamedDecl {
   /// Returns the location of this using declaration's identifier.
   SourceLocation getIdentLocation() const { return getLocation(); }
 
-  static UsingDirectiveDecl *Create(ASTContext &C, DeclContext *DC,
-                                    SourceLocation UsingLoc,
-                                    SourceLocation NamespaceLoc,
-                                    NestedNameSpecifierLoc QualifierLoc,
-                                    SourceLocation IdentLoc,
-                                    NamedDecl *Nominated,
-                                    DeclContext *CommonAncestor);
+  static UsingDirectiveDecl *
+  Create(ASTContext &C, DeclContext *DC, SourceLocation UsingLoc,
+         SourceLocation NamespaceLoc, NestedNameSpecifierLoc QualifierLoc,
+         SourceLocation IdentLoc, NamedDecl *Nominated,
+         DeclContext *CommonAncestor);
   static UsingDirectiveDecl *CreateDeserialized(ASTContext &C, GlobalDeclID 
ID);
 
   SourceRange getSourceRange() const override LLVM_READONLY {
@@ -3273,18 +3254,14 @@ class NamespaceAliasDecl : public NamespaceBaseDecl,
   using redecl_range = redeclarable_base::redecl_range;
   using redecl_iterator = redeclarable_base::redecl_iterator;
 
+  using redeclarable_base::getMostRecentDecl;
+  using redeclarable_base::getPreviousDecl;
+  using redeclarable_base::redecls;
   using redeclarable_base::redecls_begin;
   using redeclarable_base::redecls_end;
-  using redeclarable_base::redecls;
-  using redeclarable_base::getPreviousDecl;
-  using redeclarable_base::getMostRecentDecl;
 
-  NamespaceAliasDecl *getCanonicalDecl() override {
-    return getFirstDecl();
-  }
-  const NamespaceAliasDecl *getCanonicalDecl() const {
-    return getFirstDecl();
-  }
+  NamespaceAliasDecl *getCanonicalDecl() override { return getFirstDecl(); }
+  const NamespaceAliasDecl *getCanonicalDecl() const { return getFirstDecl(); }
 
   /// Retrieve the nested-name-specifier that qualifies the
   /// name of the namespace, with source-location information.
@@ -3443,9 +3420,7 @@ class UsingShadowDecl : public NamedDecl, public 
Redeclarable<UsingShadowDecl> {
     return getNextRedeclaration();
   }
 
-  UsingShadowDecl *getPreviousDeclImpl() override {
-    return getPreviousDecl();
-  }
+  UsingShadowDecl *getPreviousDeclImpl() override { return getPreviousDecl(); }
 
   UsingShadowDecl *getMostRecentDeclImpl() override {
     return getMostRecentDecl();
@@ -3473,19 +3448,15 @@ class UsingShadowDecl : public NamedDecl, public 
Redeclarable<UsingShadowDecl> {
   using redecl_range = redeclarable_base::redecl_range;
   using redecl_iterator = redeclarable_base::redecl_iterator;
 
-  using redeclarable_base::redecls_begin;
-  using redeclarable_base::redecls_end;
-  using redeclarable_base::redecls;
-  using redeclarable_base::getPreviousDecl;
   using redeclarable_base::getMostRecentDecl;
+  using redeclarable_base::getPreviousDecl;
   using redeclarable_base::isFirstDecl;
+  using redeclarable_base::redecls;
+  using redeclarable_base::redecls_begin;
+  using redeclarable_base::redecls_end;
 
-  UsingShadowDecl *getCanonicalDecl() override {
-    return getFirstDecl();
-  }
-  const UsingShadowDecl *getCanonicalDecl() const {
-    return getFirstDecl();
-  }
+  UsingShadowDecl *getCanonicalDecl() override { return getFirstDecl(); }
+  const UsingShadowDecl *getCanonicalDecl() const { return getFirstDecl(); }
 
   /// Gets the underlying declaration which has been brought into the
   /// local scope.
@@ -3770,9 +3741,7 @@ class ConstructorUsingShadowDecl final : public 
UsingShadowDecl {
   const CXXRecordDecl *getParent() const {
     return cast<CXXRecordDecl>(getDeclContext());
   }
-  CXXRecordDecl *getParent() {
-    return cast<CXXRecordDecl>(getDeclContext());
-  }
+  CXXRecordDecl *getParent() { return cast<CXXRecordDecl>(getDeclContext()); }
   //@}
 
   /// Get the inheriting constructor declaration for the direct base
@@ -3803,9 +3772,7 @@ class ConstructorUsingShadowDecl final : public 
UsingShadowDecl {
 
   /// Returns \c true if the constructed base class is a virtual base
   /// class subobject of this declaration's class.
-  bool constructsVirtualBase() const {
-    return IsVirtual;
-  }
+  bool constructsVirtualBase() const { return IsVirtual; }
 
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == ConstructorUsingShadow; }
@@ -3828,8 +3795,8 @@ class UsingEnumDecl : public BaseUsingDecl, public 
Mergeable<UsingEnumDecl> {
 
   UsingEnumDecl(DeclContext *DC, DeclarationName DN, SourceLocation UL,
                 SourceLocation EL, SourceLocation NL, TypeSourceInfo *EnumType)
-      : BaseUsingDecl(UsingEnum, DC, NL, DN), UsingLocation(UL), 
EnumLocation(EL),
-        EnumType(EnumType){}
+      : BaseUsingDecl(UsingEnum, DC, NL, DN), UsingLocation(UL),
+        EnumLocation(EL), EnumType(EnumType) {}
 
   void anchor() override;
 
@@ -3851,12 +3818,8 @@ class UsingEnumDecl : public BaseUsingDecl, public 
Mergeable<UsingEnumDecl> {
     return getEnumTypeLoc().getPrefix();
   }
   // Returns the "qualifier::Name" part as a TypeLoc.
-  TypeLoc getEnumTypeLoc() const {
-    return EnumType->getTypeLoc();
-  }
-  TypeSourceInfo *getEnumType() const {
-    return EnumType;
-  }
+  TypeLoc getEnumTypeLoc() const { return EnumType->getTypeLoc(); }
+  TypeSourceInfo *getEnumType() const { return EnumType; }
   void setEnumType(TypeSourceInfo *TSI) { EnumType = TSI; }
 
 public:
@@ -3898,7 +3861,8 @@ class UsingEnumDecl : public BaseUsingDecl, public 
Mergeable<UsingEnumDecl> {
 /// 'operator T' (which contains an unexpanded pack), but the individual
 /// UsingDecls and UsingShadowDecls will have more reasonable names.
 class UsingPackDecl final
-    : public NamedDecl, public Mergeable<UsingPackDecl>,
+    : public NamedDecl,
+      public Mergeable<UsingPackDecl>,
       private llvm::TrailingObjects<UsingPackDecl, NamedDecl *> {
   /// The UnresolvedUsingValueDecl or UnresolvedUsingTypenameDecl from
   /// which this waas instantiated.
@@ -3985,8 +3949,8 @@ class UnresolvedUsingValueDecl : public ValueDecl,
                            NestedNameSpecifierLoc QualifierLoc,
                            const DeclarationNameInfo &NameInfo,
                            SourceLocation EllipsisLoc)
-      : ValueDecl(UnresolvedUsingValue, DC,
-                  NameInfo.getLoc(), NameInfo.getName(), Ty),
+      : ValueDecl(UnresolvedUsingValue, DC, NameInfo.getLoc(),
+                  NameInfo.getName(), Ty),
         UsingLocation(UsingLoc), EllipsisLoc(EllipsisLoc),
         QualifierLoc(QualifierLoc), DNLoc(NameInfo.getInfo()) {}
 
@@ -4019,19 +3983,16 @@ class UnresolvedUsingValueDecl : public ValueDecl,
   }
 
   /// Determine whether this is a pack expansion.
-  bool isPackExpansion() const {
-    return EllipsisLoc.isValid();
-  }
+  bool isPackExpansion() const { return EllipsisLoc.isValid(); }
 
   /// Get the location of the ellipsis if this is a pack expansion.
-  SourceLocation getEllipsisLoc() const {
-    return EllipsisLoc;
-  }
+  SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
 
-  static UnresolvedUsingValueDecl *
-    Create(ASTContext &C, DeclContext *DC, SourceLocation UsingLoc,
-           NestedNameSpecifierLoc QualifierLoc,
-           const DeclarationNameInfo &NameInfo, SourceLocation EllipsisLoc);
+  static UnresolvedUsingValueDecl *Create(ASTContext &C, DeclContext *DC,
+                                          SourceLocation UsingLoc,
+                                          NestedNameSpecifierLoc QualifierLoc,
+                                          const DeclarationNameInfo &NameInfo,
+                                          SourceLocation EllipsisLoc);
 
   static UnresolvedUsingValueDecl *CreateDeserialized(ASTContext &C,
                                                       GlobalDeclID ID);
@@ -4081,10 +4042,10 @@ class UnresolvedUsingTypenameDecl
                               SourceLocation TargetNameLoc,
                               IdentifierInfo *TargetName,
                               SourceLocation EllipsisLoc)
-    : TypeDecl(UnresolvedUsingTypename, DC, TargetNameLoc, TargetName,
-               UsingLoc),
-      TypenameLocation(TypenameLoc), EllipsisLoc(EllipsisLoc),
-      QualifierLoc(QualifierLoc) {}
+      : TypeDecl(UnresolvedUsingTypename, DC, TargetNameLoc, TargetName,
+                 UsingLoc),
+        TypenameLocation(TypenameLoc), EllipsisLoc(EllipsisLoc),
+        QualifierLoc(QualifierLoc) {}
 
   void anchor() override;
 
@@ -4109,20 +4070,16 @@ class UnresolvedUsingTypenameDecl
   }
 
   /// Determine whether this is a pack expansion.
-  bool isPackExpansion() const {
-    return EllipsisLoc.isValid();
-  }
+  bool isPackExpansion() const { return EllipsisLoc.isValid(); }
 
   /// Get the location of the ellipsis if this is a pack expansion.
-  SourceLocation getEllipsisLoc() const {
-    return EllipsisLoc;
-  }
+  SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
 
   static UnresolvedUsingTypenameDecl *
-    Create(ASTContext &C, DeclContext *DC, SourceLocation UsingLoc,
-           SourceLocation TypenameLoc, NestedNameSpecifierLoc QualifierLoc,
-           SourceLocation TargetNameLoc, DeclarationName TargetName,
-           SourceLocation EllipsisLoc);
+  Create(ASTContext &C, DeclContext *DC, SourceLocation UsingLoc,
+         SourceLocation TypenameLoc, NestedNameSpecifierLoc QualifierLoc,
+         SourceLocation TargetNameLoc, DeclarationName TargetName,
+         SourceLocation EllipsisLoc);
 
   static UnresolvedUsingTypenameDecl *CreateDeserialized(ASTContext &C,
                                                          GlobalDeclID ID);
@@ -4385,6 +4342,7 @@ class MSPropertyDecl : public DeclaratorDecl {
         GetterId(Getter), SetterId(Setter) {}
 
   void anchor() override;
+
 public:
   friend class ASTDeclReader;
 
@@ -4397,9 +4355,9 @@ class MSPropertyDecl : public DeclaratorDecl {
   static bool classof(const Decl *D) { return D->getKind() == MSProperty; }
 
   bool hasGetter() const { return GetterId != nullptr; }
-  IdentifierInfo* getGetterId() const { return GetterId; }
+  IdentifierInfo *getGetterId() const { return GetterId; }
   bool hasSetter() const { return SetterId != nullptr; }
-  IdentifierInfo* getSetterId() const { return SetterId; }
+  IdentifierInfo *getSetterId() const { return SetterId; }
 };
 
 /// Parts of a decomposed MSGuidDecl. Factored out to avoid unnecessary
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index f0da56542ae7e..476eb0bc2d5f3 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -76,7 +76,8 @@ void LazyASTUnresolvedSet::getFromExternalSource(ASTContext 
&C) const {
 CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
     : UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0),
       Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false),
-      Abstract(false), IsStandardLayout(true), IsCXX11StandardLayout(true),
+      Abstract(false), IsStandardLayout(true),
+      IsStandardLayoutUnionMember(false), IsCXX11StandardLayout(true),
       HasBasesWithFields(false), HasBasesWithNonStaticDataMembers(false),
       HasPrivateFields(false), HasProtectedFields(false),
       HasPublicFields(false), HasMutableFields(false), 
HasVariantMembers(false),
@@ -2321,6 +2322,19 @@ void 
CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
     data().IsStandardLayout = false;
     data().IsCXX11StandardLayout = false;
   }
+
+  if (isUnion() && isStandardLayout()) {
+    for (const FieldDecl *FD : fields()) {
+      if (const auto *RT =
+              FD->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) {
+        if (auto *CRD =
+                dyn_cast<CXXRecordDecl>(RT->getDecl()->getDefinitionOrSelf())) 
{
+          if (CRD->hasDefinition() && CRD->isStandardLayout())
+            CRD->setIsStandardLayoutUnionMember(true);
+        }
+      }
+    }
+  }
 }
 
 bool CXXRecordDecl::mayBeAbstract() const {
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 02864621d60a3..be6985f8eb4c9 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -3241,7 +3241,8 @@ static bool canUseCtorHoming(const CXXRecordDecl *RD) {
 
   if (RD->isLambda() || RD->isAggregate() ||
       RD->hasTrivialDefaultConstructor() ||
-      RD->hasConstexprNonCopyMoveConstructor())
+      RD->hasConstexprNonCopyMoveConstructor() ||
+      RD->isStandardLayoutUnionMember())
     return false;
 
   for (const CXXConstructorDecl *Ctor : RD->ctors()) {
diff --git a/clang/test/DebugInfo/CXX/limited-ctor.cpp 
b/clang/test/DebugInfo/CXX/limited-ctor.cpp
index e820c0703df4f..3fe1682358af2 100644
--- a/clang/test/DebugInfo/CXX/limited-ctor.cpp
+++ b/clang/test/DebugInfo/CXX/limited-ctor.cpp
@@ -53,7 +53,7 @@ struct DeclaredConstexpr {
 template <class A, class B> struct Aliased {
   A first;
   B second;
-  constexpr Aliased(const A &a, const B &b) : first(a), second(b) {}
+  Aliased(const A &a, const B &b) : first(a), second(b) {}
 };
 union AliasedSlot {
   Aliased<const int, int> value;
@@ -66,6 +66,24 @@ int ReadAliasedSlot() {
   return TestAliasedSlot.value.first;
 }
 
+// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: 
"ConstexprAliased<int, int>"{{.*}}DIFlagTypePassByValue
+// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: 
"ConstexprAliased<const int, int>"{{.*}}DIFlagTypePassByValue
+template <class A, class B> struct ConstexprAliased {
+  A first;
+  B second;
+  constexpr ConstexprAliased(const A &a, const B &b) : first(a), second(b) {}
+};
+union ConstexprAliasedSlot {
+  ConstexprAliased<const int, int> value;
+  ConstexprAliased<int, int> mutable_value;
+  ConstexprAliasedSlot() {}
+  ~ConstexprAliasedSlot() {}
+} TestConstexprAliasedSlot;
+int ReadConstexprAliasedSlot() {
+  TestConstexprAliasedSlot.mutable_value = ConstexprAliased<int, int>(1, 2);
+  return TestConstexprAliasedSlot.value.first;
+}
+
 // Defined out-of-line constexpr constructor should emit full debug info.
 // CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: 
"OutOfLineConstexpr"{{.*}}DIFlagTypePassByValue
 struct OutOfLineConstexpr {
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index 195b8ab4c4e66..6fa1da7f01f00 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -24,10 +24,12 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/Linkage.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/ASTUnit.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Testing/Annotations/Annotations.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <cassert>
 #include <memory>
@@ -36,6 +38,14 @@
 using namespace clang::ast_matchers;
 using namespace clang::tooling;
 using namespace clang;
+using ::testing::AllOf;
+using ::testing::Not;
+using ::testing::Pointee;
+
+MATCHER(IsStandardLayout, "") { return arg.isStandardLayout(); }
+MATCHER(IsStandardLayoutUnionMember, "") {
+  return arg.isStandardLayoutUnionMember();
+}
 
 TEST(Decl, CleansUpAPValues) {
   MatchFinder Finder;
@@ -1029,3 +1039,249 @@ TEST(Decl, ObjCPropertyDeclNameForDiagnostic) {
                              /*Qualified=*/true);
   EXPECT_EQ(ExtPQualifiedOS.str(), "-[MyClass extensionProp]");
 }
+
+class StandardLayoutUnionMemberTest : public ::testing::Test {
+protected:
+  std::unique_ptr<ASTUnit> AST;
+  ASTContext *Ctx = nullptr;
+
+  void buildAST(StringRef Code, ArrayRef<std::string> Args = {"-std=c++11"}) {
+    AST = tooling::buildASTFromCodeWithArgs(Code, Args);
+    ASSERT_NE(AST, nullptr);
+    Ctx = &AST->getASTContext();
+  }
+
+  const CXXRecordDecl *findRecord(StringRef Name) const {
+    assert(Ctx && "ASTContext not initialized");
+    return selectFirst<CXXRecordDecl>(
+        "d",
+        match(cxxRecordDecl(hasName(Name), isDefinition()).bind("d"), *Ctx));
+  }
+
+  const CXXRecordDecl *findTemplate(StringRef Name,
+                                    StringRef TemplateArg) const {
+    assert(Ctx && "ASTContext not initialized");
+    return selectFirst<CXXRecordDecl>(
+        "d", match(classTemplateSpecializationDecl(
+                       hasName(Name), isDefinition(),
+                       hasTemplateArgument(
+                           0, refersToType(asString(TemplateArg.str()))))
+                       .bind("d"),
+                   *Ctx));
+  }
+
+  // Matches a standard-layout union member.
+  auto IsSLUnionMember() {
+    return Pointee(AllOf(IsStandardLayout(), IsStandardLayoutUnionMember()));
+  }
+
+  // Matches a standard-layout type that is not a member of a standard-layout
+  // union.
+  auto IsStandaloneSL() {
+    return Pointee(
+        AllOf(IsStandardLayout(), Not(IsStandardLayoutUnionMember())));
+  }
+
+  // Matches a non-standard-layout type.
+  auto IsNonSL() {
+    return Pointee(
+        AllOf(Not(IsStandardLayout()), Not(IsStandardLayoutUnionMember())));
+  }
+};
+
+TEST_F(StandardLayoutUnionMemberTest, StandaloneStruct) {
+  buildAST(R"cc(
+    struct StandaloneSL {
+      int x;
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("StandaloneSL"), IsStandaloneSL());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, NonStandardLayoutStruct) {
+  buildAST(R"cc(
+    struct NonSL {
+      virtual void foo();
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("NonSL"), IsNonSL());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, StandardLayoutUnion) {
+  buildAST(R"cc(
+    struct SLInUnion {
+      int x;
+    };
+
+    union SLUnion {
+      SLInUnion u;
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("SLUnion"), IsStandaloneSL());
+  EXPECT_THAT(findRecord("SLInUnion"), IsSLUnionMember());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, TemplatedMember) {
+  buildAST(R"cc(
+    template <typename T>
+    struct TemplatedSL {
+      T x;
+    };
+
+    union TemplatedUnion {
+      TemplatedSL<int> a;
+      TemplatedSL<float> b;
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("TemplatedUnion"), IsStandaloneSL());
+  EXPECT_THAT(findTemplate("TemplatedSL", "int"), IsSLUnionMember());
+  EXPECT_THAT(findTemplate("TemplatedSL", "float"), IsSLUnionMember());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, NonStandardLayoutUnion) {
+  buildAST(R"cc(
+    struct NonSL {
+      virtual void foo();
+    };
+
+    struct SLInNonSLUnion {
+      int x;
+    };
+
+    union NonSLUnion {
+      SLInNonSLUnion s;
+      NonSL n;
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("NonSLUnion"), Pointee(Not(IsStandardLayout())));
+  EXPECT_THAT(findRecord("NonSL"), IsNonSL());
+  EXPECT_THAT(findRecord("SLInNonSLUnion"), IsStandaloneSL());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, NestedStruct) {
+  buildAST(R"cc(
+    union NestedUnion {
+      struct NestedSL {
+        int a;
+      } n;
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("NestedUnion"), IsStandaloneSL());
+  EXPECT_THAT(findRecord("NestedSL"), IsSLUnionMember());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, Array) {
+  buildAST(R"cc(
+    struct SLInArray {
+      int x;
+    };
+    struct SLInMultiArray {
+      int y;
+    };
+    union ArrayUnion {
+      SLInArray arr[3];
+      SLInMultiArray multi_arr[2][4];
+      int raw;
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("ArrayUnion"), IsStandaloneSL());
+  EXPECT_THAT(findRecord("SLInArray"), IsSLUnionMember());
+  EXPECT_THAT(findRecord("SLInMultiArray"), IsSLUnionMember());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, CVQualified) {
+  buildAST(R"cc(
+    struct SLConst {
+      int x;
+    };
+    struct SLVolatile {
+      int y;
+    };
+    union CVUnion {
+      const SLConst c;
+      volatile SLVolatile v;
+      int raw;
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("CVUnion"), IsStandaloneSL());
+  EXPECT_THAT(findRecord("SLConst"), IsSLUnionMember());
+  EXPECT_THAT(findRecord("SLVolatile"), IsSLUnionMember());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, GenericUnionTemplate) {
+  buildAST(R"cc(
+    template <typename T>
+    union GenericUnion {
+      T val;
+      int raw;
+    };
+    struct SLInGenericUnion {
+      int x;
+    };
+    GenericUnion<SLInGenericUnion> u;
+  )cc");
+
+  EXPECT_THAT(findRecord("SLInGenericUnion"), IsSLUnionMember());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, AnonymousUnion) {
+  buildAST(R"cc(
+    struct SLInAnonUnion {
+      int x;
+    };
+    struct EnclosingStruct {
+      union {
+        SLInAnonUnion a;
+        int b;
+      };
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("SLInAnonUnion"), IsSLUnionMember());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, Inheritance) {
+  buildAST(R"cc(
+    struct EmptyBase {};
+    struct SLDerived : EmptyBase {
+      int x;
+    };
+    union DerivedUnion {
+      SLDerived d;
+      int raw;
+    };
+  )cc");
+
+  EXPECT_THAT(findRecord("DerivedUnion"), IsStandaloneSL());
+  EXPECT_THAT(findRecord("SLDerived"), IsSLUnionMember());
+  EXPECT_THAT(findRecord("EmptyBase"), IsStandaloneSL());
+}
+
+TEST_F(StandardLayoutUnionMemberTest, AnonymousTypedefStruct) {
+  buildAST(R"cc(
+    typedef struct {
+      int x;
+    } AnonTypedefSL;
+
+    union AnonTypedefUnion {
+      AnonTypedefSL s;
+      int raw;
+    };
+  )cc");
+
+  const auto *TD = selectFirst<TypedefDecl>(
+      "d", match(typedefDecl(hasName("AnonTypedefSL")).bind("d"), *Ctx));
+  ASSERT_NE(TD, nullptr);
+  const auto *AnonRecord = TD->getUnderlyingType()->getAsCXXRecordDecl();
+
+  EXPECT_THAT(findRecord("AnonTypedefUnion"), IsStandaloneSL());
+  EXPECT_THAT(AnonRecord, IsSLUnionMember());
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to