https://github.com/yronglin created 
https://github.com/llvm/llvm-project/pull/221299

None

>From f8adc7acf60a72ff1238f76d96474d6c355cb4d6 Mon Sep 17 00:00:00 2001
From: yronglin <[email protected]>
Date: Thu, 27 Aug 2026 06:48:32 -0700
Subject: [PATCH] [clang][AST] Copy default-member-init wherever it is used and
 add a tag to its initialization init-list node

Signed-off-by: yronglin <[email protected]>
---
 clang/include/clang/AST/Expr.h                |  24 ++
 clang/include/clang/AST/ExprCXX.h             |   8 +
 clang/include/clang/AST/JSONNodeDumper.h      |   1 +
 clang/include/clang/AST/Stmt.h                |   5 +
 clang/include/clang/AST/TextNodeDumper.h      |   1 +
 clang/include/clang/Analysis/CFG.h            | 103 +++++++-
 .../FlowSensitive/DataflowEnvironment.h       |  66 ++---
 clang/lib/AST/ByteCode/Compiler.cpp           |  81 +++---
 clang/lib/AST/ByteCode/Compiler.h             |   9 +
 clang/lib/AST/Expr.cpp                        |   9 +
 clang/lib/AST/JSONNodeDumper.cpp              |  10 +
 clang/lib/AST/TextNodeDumper.cpp              |  12 +
 clang/lib/Analysis/CFG.cpp                    |  45 +++-
 .../lib/Analysis/FlowSensitive/AdornedCFG.cpp |   5 +
 .../FlowSensitive/DataflowEnvironment.cpp     | 135 +---------
 clang/lib/Analysis/FlowSensitive/Transfer.cpp |   8 +-
 .../TypeErasedDataflowAnalysis.cpp            |  10 +
 clang/lib/Analysis/PathDiagnostic.cpp         |   4 +
 clang/lib/Sema/SemaExpr.cpp                   |  53 +++-
 clang/lib/Sema/SemaInit.cpp                   |   7 +
 clang/lib/Serialization/ASTReaderStmt.cpp     |   3 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |   2 +
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |   5 +
 .../lib/StaticAnalyzer/Core/SymbolManager.cpp |   3 +
 .../AST/ast-dump-uses-default-member-init.cpp |  52 ++++
 .../FlowSensitive/DataflowEnvironmentTest.cpp | 247 ------------------
 .../Analysis/FlowSensitive/TransferTest.cpp   | 122 +++++++++
 27 files changed, 559 insertions(+), 471 deletions(-)
 create mode 100644 clang/test/AST/ast-dump-uses-default-member-init.cpp

diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 535086a6c2aa3..2699a08d6ef18 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -479,6 +479,24 @@ class Expr : public ValueStmt {
   /// an aspect of the value-kind type system.
   bool refersToBitField() const { return getObjectKind() == OK_BitField; }
 
+  /// Whether this is a list-initialization (an \c InitListExpr or a
+  /// \c CXXParenListInitExpr) that initializes an object using a default
+  /// member initializer, that is, one of whose initializers is a
+  /// \c CXXDefaultInitExpr.
+  ///
+  /// Within such an initializer, \c this denotes the object that this
+  /// list-initialization initializes, not the instance pointer of the
+  /// enclosing member function:
+  ///
+  /// \code
+  ///   struct S { int x; int y = this->x; };
+  ///   int foo() { return S{10}.y; }  // `this` denotes the `S{10}` object
+  /// \endcode
+  ///
+  /// Sema sets this when it fills the list in, so that consumers do not each
+  /// have to re-derive which list-initializations establish such an object.
+  bool initializesObjectWithDefaultMemberInit() const;
+
   /// If this expression refers to a bit-field, retrieve the
   /// declaration of that bit-field.
   ///
@@ -5380,6 +5398,7 @@ class InitListExpr : public Expr {
   explicit InitListExpr(EmptyShell Empty)
       : Expr(InitListExprClass, Empty), AltForm(nullptr, true) {
     InitListExprBits.IsExplicit = false;
+    InitListExprBits.InitializesObjectWithDefaultMemberInit = false;
   }
 
   unsigned getNumInits() const { return InitExprs.size(); }
@@ -5494,6 +5513,11 @@ class InitListExpr : public Expr {
   // locations). Implicit InitListExpr's are created by the semantic analyzer.
   bool isExplicit() const { return InitListExprBits.IsExplicit; }
 
+  /// See Expr::initializesObjectWithDefaultMemberInit().
+  void setInitializesObjectWithDefaultMemberInit(bool V = true) {
+    InitListExprBits.InitializesObjectWithDefaultMemberInit = V;
+  }
+
   /// Is this an initializer for an array of characters, initialized by a 
string
   /// literal or an @encode?
   bool isStringLiteralInit() const;
diff --git a/clang/include/clang/AST/ExprCXX.h 
b/clang/include/clang/AST/ExprCXX.h
index bb790b1100e7f..9e6956a9d703d 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5193,9 +5193,12 @@ class CXXParenListInitExpr final
   friend class TrailingObjects;
   friend class ASTStmtReader;
   friend class ASTStmtWriter;
+  friend class Expr;
 
   unsigned NumExprs;
   unsigned NumUserSpecifiedExprs;
+  /// See Expr::initializesObjectWithDefaultMemberInit().
+  bool InitializesObjectWithDefaultMemberInit = false;
   SourceLocation InitLoc, LParenLoc, RParenLoc;
   llvm::PointerUnion<Expr *, FieldDecl *> ArrayFillerOrUnionFieldInit;
 
@@ -5229,6 +5232,11 @@ class CXXParenListInitExpr final
 
   void updateDependence() { setDependence(computeDependence(this)); }
 
+  /// See Expr::initializesObjectWithDefaultMemberInit().
+  void setInitializesObjectWithDefaultMemberInit(bool V = true) {
+    InitializesObjectWithDefaultMemberInit = V;
+  }
+
   MutableArrayRef<Expr *> getInitExprs() {
     return getTrailingObjects(NumExprs);
   }
diff --git a/clang/include/clang/AST/JSONNodeDumper.h 
b/clang/include/clang/AST/JSONNodeDumper.h
index 679ce4e4815ae..0205362195111 100644
--- a/clang/include/clang/AST/JSONNodeDumper.h
+++ b/clang/include/clang/AST/JSONNodeDumper.h
@@ -309,6 +309,7 @@ class JSONNodeDumper
   void VisitCXXTypeidExpr(const CXXTypeidExpr *CTE);
   void VisitConstantExpr(const ConstantExpr *CE);
   void VisitInitListExpr(const InitListExpr *ILE);
+  void VisitCXXParenListInitExpr(const CXXParenListInitExpr *PLIE);
   void VisitGenericSelectionExpr(const GenericSelectionExpr *GSE);
   void VisitCXXUnresolvedConstructExpr(const CXXUnresolvedConstructExpr *UCE);
   void VisitCXXConstructExpr(const CXXConstructExpr *CE);
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 5d27ded64082d..93fb9838c9538 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -670,6 +670,7 @@ class alignas(void *) Stmt {
 
   class InitListExprBitfields {
     friend class ASTStmtReader;
+    friend class Expr;
     friend class InitListExpr;
 
     LLVM_PREFERRED_TYPE(ExprBitfields)
@@ -682,6 +683,10 @@ class alignas(void *) Stmt {
     // Whether this list is explicitly written in the source (with braces).
     LLVM_PREFERRED_TYPE(bool)
     unsigned IsExplicit : 1;
+    /// Whether this list initializes an object using a default member
+    /// initializer. See 
InitListExpr::initializesObjectWithDefaultMemberInit().
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned InitializesObjectWithDefaultMemberInit : 1;
   };
 
   class ParenListExprBitfields {
diff --git a/clang/include/clang/AST/TextNodeDumper.h 
b/clang/include/clang/AST/TextNodeDumper.h
index 7219a0d3f8e50..cb5e2c694fca1 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -287,6 +287,7 @@ class TextNodeDumper
   void VisitFloatingLiteral(const FloatingLiteral *Node);
   void VisitStringLiteral(const StringLiteral *Str);
   void VisitInitListExpr(const InitListExpr *ILE);
+  void VisitCXXParenListInitExpr(const CXXParenListInitExpr *PLIE);
   void VisitGenericSelectionExpr(const GenericSelectionExpr *E);
   void VisitUnaryOperator(const UnaryOperator *Node);
   void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Node);
diff --git a/clang/include/clang/Analysis/CFG.h 
b/clang/include/clang/Analysis/CFG.h
index 749ed3fc76452..0cf25bdaf8de3 100644
--- a/clang/include/clang/Analysis/CFG.h
+++ b/clang/include/clang/Analysis/CFG.h
@@ -51,6 +51,16 @@ class FieldDecl;
 class LangOptions;
 class VarDecl;
 
+/// The pointers a `CFGElement` stores are AST nodes and `CFGElement`s of AST
+/// nodes, all of which are allocated by `ASTContext`'s allocator with at least
+/// 8-byte alignment. Three low bits are therefore available for the kind; the
+/// alignment itself is checked by `llvm::PointerIntPair`.
+struct CFGElementPtrTraits {
+  static void *getAsVoidPointer(const void *P) { return const_cast<void *>(P); 
}
+  static const void *getFromVoidPointer(void *P) { return P; }
+  static constexpr int NumLowBitsAvailable = 3;
+};
+
 /// Represents a top-level expression in a basic block.
 class CFGElement {
 public:
@@ -81,16 +91,22 @@ class CFGElement {
     DTOR_BEGIN = AutomaticObjectDtor,
     DTOR_END = TemporaryDtor,
     CleanupFunction,
+    // list-initialized object marker kind
+    ListInitObjectBegin,
+    ListInitObjectEnd,
+    LIST_INIT_OBJECT_BEGIN = ListInitObjectBegin,
+    LIST_INIT_OBJECT_END = ListInitObjectEnd,
   };
 
 protected:
-  // The int bits are used to mark the kind.
-  llvm::PointerIntPair<const void *, 2> Data1;
-  llvm::PointerIntPair<const void *, 2> Data2;
+  // The int bits are used to mark the kind: three bits in each of the two
+  // pointers, so up to 64 kinds.
+  llvm::PointerIntPair<const void *, 3, unsigned, CFGElementPtrTraits> Data1;
+  llvm::PointerIntPair<const void *, 3, unsigned, CFGElementPtrTraits> Data2;
 
   CFGElement(Kind kind, const void *Ptr1, const void *Ptr2 = nullptr)
-      : Data1(Ptr1, ((unsigned)kind) & 0x3),
-        Data2(Ptr2, (((unsigned)kind) >> 2) & 0x3) {
+      : Data1(Ptr1, ((unsigned)kind) & 0x7),
+        Data2(Ptr2, (((unsigned)kind) >> 3) & 0x7) {
     assert(getKind() == kind);
   }
 
@@ -121,7 +137,7 @@ class CFGElement {
 
   Kind getKind() const {
     unsigned x = Data2.getInt();
-    x <<= 2;
+    x <<= 3;
     x |= Data1.getInt();
     return (Kind) x;
   }
@@ -409,6 +425,68 @@ class CFGScopeEnd : public CFGScopeMarker {
   }
 };
 
+/// Delimits the region of a list-initialization that establishes the object
+/// which `this` denotes inside the default member initializers it uses.
+///
+/// A `ListInitObjectBegin` precedes every element of the list-initialization,
+/// including the default member initializers themselves, and the matching
+/// `ListInitObjectEnd` follows them. This re-expresses, in the linearized CFG,
+/// the nesting that the AST encodes by containment: consumers that walk the
+/// CFG can carry the object forward from the marker instead of searching for
+/// the enclosing list-initialization.
+///
+/// Only emitted when `BuildOptions::AddCXXDefaultInitExprInAggregates` is set,
+/// since otherwise the default member initializers are not in the CFG at all.
+class CFGListInitObject : public CFGElement {
+public:
+  /// The `InitListExpr` or `CXXParenListInitExpr` that establishes the object.
+  LLVM_ATTRIBUTE_RETURNS_NONNULL const Expr *getListInitExpr() const {
+    return static_cast<const Expr *>(Data1.getPointer());
+  }
+
+protected:
+  CFGListInitObject() = default;
+  CFGListInitObject(Kind K, const Expr *E) : CFGElement(K, E) {
+    assert(isKind(*this));
+  }
+
+private:
+  friend class CFGElement;
+
+  static bool isKind(const CFGElement &E) {
+    return E.getKind() >= LIST_INIT_OBJECT_BEGIN &&
+           E.getKind() <= LIST_INIT_OBJECT_END;
+  }
+};
+
+class CFGListInitObjectBegin : public CFGListInitObject {
+public:
+  CFGListInitObjectBegin() {}
+  explicit CFGListInitObjectBegin(const Expr *E)
+      : CFGListInitObject(ListInitObjectBegin, E) {}
+
+private:
+  friend class CFGElement;
+
+  static bool isKind(const CFGElement &E) {
+    return E.getKind() == ListInitObjectBegin;
+  }
+};
+
+class CFGListInitObjectEnd : public CFGListInitObject {
+public:
+  CFGListInitObjectEnd() {}
+  explicit CFGListInitObjectEnd(const Expr *E)
+      : CFGListInitObject(ListInitObjectEnd, E) {}
+
+private:
+  friend class CFGElement;
+
+  static bool isKind(const CFGElement &E) {
+    return E.getKind() == ListInitObjectEnd;
+  }
+};
+
 /// Represents C++ object destructor implicitly generated by compiler on 
various
 /// occasions.
 class CFGImplicitDtor : public CFGElement {
@@ -1206,6 +1284,14 @@ class CFGBlock {
     Elements.push_back(CFGScopeEnd(VD, S), C);
   }
 
+  void appendListInitObjectBegin(const Expr *E, BumpVectorContext &C) {
+    Elements.push_back(CFGListInitObjectBegin(E), C);
+  }
+
+  void appendListInitObjectEnd(const Expr *E, BumpVectorContext &C) {
+    Elements.push_back(CFGListInitObjectEnd(E), C);
+  }
+
   void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) {
     Elements.push_back(CFGBaseDtor(BS), C);
   }
@@ -1301,6 +1387,11 @@ class CFG {
     bool AddCXXNewAllocator = false;
     bool AddCXXDefaultInitExprInCtors = false;
     bool AddCXXDefaultInitExprInAggregates = false;
+    /// Bracket each list-initialization that establishes an object for the
+    /// default member initializers it uses with CFGListInitObjectBegin/End.
+    /// Requires AddCXXDefaultInitExprInAggregates, since otherwise those
+    /// initializers are not in the CFG.
+    bool AddListInitObjectMarkers = false;
     bool AddRichCXXConstructors = false;
     bool MarkElidedCXXConstructors = false;
     bool AddVirtualBaseBranches = false;
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 47ac06137bb5f..10f63cefe019f 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -30,6 +30,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <cassert>
@@ -366,26 +367,37 @@ class Environment {
     return ThisPointeeLoc;
   }
 
-  /// Returns the storage location assigned to the `this` pointee in the
-  /// environment given a specific `CXXThisExpr`. Returns null if the `this`
-  /// pointee has no assigned storage location in the environment.
-  /// Note that `this` can be used in a non-member context, e.g.:
+  /// Returns the object established by the innermost list-initialization
+  /// currently being evaluated, or null if there is none.
+  ///
+  /// Within a default member initializer, `this` denotes the object being
+  /// initialized. When that initializer is used by a list-initialization
+  /// rather than by a constructor, the object is not the `this` pointee of the
+  /// enclosing member function:
   ///
   /// \code
-  ///   struct S {
-  ///     int x;
-  ///     int y = this->x;
-  ///   };
-  ///   int foo() {
-  ///     return S{10}.y;  // will have a `this` for initializing `S::y`.
-  ///   }
+  ///   struct S { int x; int y = this->x; };
+  ///   int foo() { return S{10}.y; }  // `this` denotes the `S{10}` object
   /// \endcode
-  RecordStorageLocation *
-  getThisPointeeStorageLocation(const CXXThisExpr &ThisExpr) const {
-    auto It = ThisExprOverrides->find(&ThisExpr);
-    if (It == ThisExprOverrides->end())
-      return ThisPointeeLoc;
-    return It->second;
+  ///
+  /// The CFG brackets such a list-initialization with
+  /// `CFGListInitObjectBegin`/`CFGListInitObjectEnd`, which push and pop this
+  /// stack, so the object is available while its elements are evaluated.
+  RecordStorageLocation *getListInitObjectLocation() const {
+    return ListInitObjects.empty() ? nullptr : ListInitObjects.back();
+  }
+
+  /// Pushes the object established by a list-initialization. Called for
+  /// `CFGListInitObjectBegin`.
+  void pushListInitObject(RecordStorageLocation &Loc) {
+    ListInitObjects.push_back(&Loc);
+  }
+
+  /// Pops the object established by a list-initialization. Called for
+  /// `CFGListInitObjectEnd`.
+  void popListInitObject() {
+    assert(!ListInitObjects.empty());
+    ListInitObjects.pop_back();
   }
 
   /// Sets the storage location assigned to the `this` pointee in the
@@ -717,8 +729,6 @@ class Environment {
 private:
   using PrValueToResultObject =
       llvm::DenseMap<const Expr *, RecordStorageLocation *>;
-  using ThisExprOverridesMap =
-      llvm::DenseMap<const CXXThisExpr *, RecordStorageLocation *>;
 
   // The copy-constructor is for use in fork() only.
   Environment(const Environment &) = default;
@@ -782,15 +792,6 @@ class Environment {
                        RecordStorageLocation *ThisPointeeLoc,
                        RecordStorageLocation *LocForRecordReturnVal);
 
-  static ThisExprOverridesMap
-  buildThisExprOverridesMap(const FunctionDecl *FuncDecl,
-                            RecordStorageLocation *ThisPointeeLoc,
-                            const PrValueToResultObject &ResultObjectMap);
-
-  static ThisExprOverridesMap
-  buildThisExprOverridesMap(Stmt *S, RecordStorageLocation *ThisPointeeLoc,
-                            const PrValueToResultObject &ResultObjectMap);
-
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
@@ -833,15 +834,14 @@ class Environment {
   //   constructed.
   RecordStorageLocation *LocForRecordReturnVal = nullptr;
 
+  // The objects established by the list-initializations currently being
+  // evaluated, innermost last. See getListInitObjectLocation().
+  llvm::SmallVector<RecordStorageLocation *, 1> ListInitObjects;
+
   // The storage location of the `this` pointee. Should only be null if the
   // analysis target is not a method.
   RecordStorageLocation *ThisPointeeLoc = nullptr;
 
-  // Maps from `CXXThisExpr`s to their storage locations, if it should be
-  // different from `ThisPointeeLoc` (for example, CXXThisExpr that are
-  // under a CXXDefaultInitExpr under an InitListExpr).
-  std::shared_ptr<ThisExprOverridesMap> ThisExprOverrides;
-
   // Maps from declarations and glvalue expression to storage locations that 
are
   // assigned to them. Unlike the maps in `DataflowAnalysisContext`, these
   // include only storage locations that are in scope for a particular basic
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index c182639ea07f8..b2bb4db26f547 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -282,6 +282,23 @@ template <class Emitter> class InitStackScope final {
   bool Active;
 };
 
+/// Scope for the object established by a list-initialization that uses a
+/// default member initializer. Mirrors
+/// CodeGenFunction::FieldConstructionScope.
+template <class Emitter> class ListInitObjectScope final {
+public:
+  ListInitObjectScope(Compiler<Emitter> *Ctx, UnsignedOrNone Ptr)
+      : Ctx(Ctx), OldValue(Ctx->ListInitObjectPtr) {
+    Ctx->ListInitObjectPtr = Ptr;
+  }
+
+  ~ListInitObjectScope() { Ctx->ListInitObjectPtr = OldValue; }
+
+private:
+  Compiler<Emitter> *Ctx;
+  UnsignedOrNone OldValue;
+};
+
 /// Scope used to handle temporaries in toplevel variable declarations.
 template <class Emitter> class DeclScope final : public LocalScope<Emitter> {
 public:
@@ -2298,6 +2315,23 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const 
Expr *> Inits,
                                       const Expr *ArrayFiller, const Expr *E) {
   InitLinkScope<Emitter> ILS(this, InitLink::InitList());
 
+  // If this list initializes an object using a default member initializer,
+  // `this` within that initializer denotes the object being initialized here.
+  // Record a pointer to it so that CXXThisExpr can simply load it; the pointer
+  // is on the stack because visitInitializer() puts it there.
+  // Note: only enter the scope when this list establishes an object. A nested
+  // list that does not must leave the enclosing one in place, since a default
+  // member initializer may contain one.
+  std::optional<ListInitObjectScope<Emitter>> LIOS;
+  if (E->initializesObjectWithDefaultMemberInit() && Initializing) {
+    unsigned Offset = this->allocateLocalPrimitive(E, PT_Ptr, 
/*IsConst=*/true);
+    if (!this->emitDupPtr(E))
+      return false;
+    if (!this->emitSetLocal(PT_Ptr, Offset, E))
+      return false;
+    LIOS.emplace(this, Offset);
+  }
+
   QualType QT = E->getType();
   if (const auto *AT = QT->getAs<AtomicType>())
     QT = AT->getValueType();
@@ -6445,57 +6479,30 @@ bool Compiler<Emitter>::VisitCXXThisExpr(const 
CXXThisExpr *E) {
   if (!InitStackActive || InitStack.empty())
     return this->emitThis(E);
 
-  // If our init stack is, for example:
-  // 0 Stack: 3 (decl)
-  // 1 Stack: 6 (init list)
-  // 2 Stack: 1 (field)
-  // 3 Stack: 6 (init list)
-  // 4 Stack: 1 (field)
-  //
-  // We want to find the LAST element in it that's an init list,
-  // which is marked with the K_InitList marker. The index right
-  // before that points to an init list. We need to find the
-  // elements before the K_InitList element that point to a base
-  // (e.g. a decl or This), optionally followed by field, elem, etc.
-  // In the example above, we want to emit elements [0..2].
-  unsigned StartIndex = 0;
-  unsigned EndIndex = 0;
-  // Find the init list.
-  for (StartIndex = InitStack.size() - 1; StartIndex > 0; --StartIndex) {
-    if (InitStack[StartIndex].Kind == InitLink::K_DIE) {
-      EndIndex = StartIndex;
-      --StartIndex;
-      break;
-    }
-  }
+  // We are inside a default member initializer (InitStackActive), so 'this'
+  // denotes the object under construction. If a list-initialization
+  // established one, it recorded a pointer to it; otherwise the path in
+  // InitStack leads to it.
+  if (ListInitObjectPtr)
+    return this->emitGetLocal(PT_Ptr, *ListInitObjectPtr, E);
 
-  // Walk backwards to find the base.
+  // Walk backwards to the base the path starts from, then replay it.
+  unsigned StartIndex = InitStack.size() - 1;
   for (; StartIndex > 0; --StartIndex) {
-    if (InitStack[StartIndex].Kind == InitLink::K_InitList)
-      continue;
-
     if (InitStack[StartIndex].Kind != InitLink::K_Field &&
         InitStack[StartIndex].Kind != InitLink::K_Elem &&
         InitStack[StartIndex].Kind != InitLink::K_Base &&
+        InitStack[StartIndex].Kind != InitLink::K_InitList &&
         InitStack[StartIndex].Kind != InitLink::K_DIE)
       break;
   }
 
-  if (StartIndex == 0 && EndIndex == 0)
-    EndIndex = InitStack.size() - 1;
-
   assert(InitStack[StartIndex].Kind == InitLink::K_Decl ||
          InitStack[StartIndex].Kind == InitLink::K_This ||
          InitStack[StartIndex].Kind == InitLink::K_Temp ||
          InitStack[StartIndex].Kind == InitLink::K_RVO);
 
-  // NOTE: This could be StartIndex < EndIndex, but we're also abusing the
-  // InitStack mechanism in visitWithSubstitutions to have the This pointer
-  // _just_ be a local variable.
-  assert(StartIndex <= EndIndex);
-
-  // Emit the instructions.
-  for (unsigned I = StartIndex; I != (EndIndex + 1); ++I) {
+  for (unsigned I = StartIndex, N = InitStack.size(); I != N; ++I) {
     if (InitStack[I].Kind == InitLink::K_InitList ||
         InitStack[I].Kind == InitLink::K_DIE)
       continue;
diff --git a/clang/lib/AST/ByteCode/Compiler.h 
b/clang/lib/AST/ByteCode/Compiler.h
index f34809cd0f14c..0dca94f7e3bb3 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -42,6 +42,7 @@ template <class Emitter> class LabelScope;
 template <class Emitter> class SwitchScope;
 template <class Emitter> class StmtExprScope;
 template <class Emitter> class LocOverrideScope;
+template <class Emitter> class ListInitObjectScope;
 
 template <class Emitter> class Compiler;
 struct InitLink {
@@ -362,6 +363,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, 
bool>,
   friend class DeclScope<Emitter>;
   friend class InitLinkScope<Emitter>;
   friend class InitStackScope<Emitter>;
+  friend class ListInitObjectScope<Emitter>;
   friend class OptionScope<Emitter>;
   friend class ArrayIndexScope<Emitter>;
   friend class SourceLocScope<Emitter>;
@@ -506,6 +508,13 @@ class Compiler : public 
ConstStmtVisitor<Compiler<Emitter>, bool>,
   llvm::SmallVector<InitLink> InitStack;
   bool InitStackActive = false;
 
+  /// Offset of a local holding a pointer to the object established by the
+  /// innermost enclosing list-initialization that uses a default member
+  /// initializer, if any. Within such an initializer, `this` denotes that
+  /// object rather than the instance pointer of the current frame. This
+  /// mirrors CodeGenFunction::CXXDefaultInitExprThis.
+  UnsignedOrNone ListInitObjectPtr = std::nullopt;
+
   /// Type of the expression returned by the function.
   OptPrimType ReturnType;
 
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 6ce0a29aa3bd7..a1720596ea315 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -2443,6 +2443,7 @@ InitListExpr::InitListExpr(const ASTContext &C, 
SourceLocation lbraceloc,
   sawArrayRangeDesignator(false);
   InitExprs.insert(C, InitExprs.end(), initExprs.begin(), initExprs.end());
   InitListExprBits.IsExplicit = isExplicit;
+  InitListExprBits.InitializesObjectWithDefaultMemberInit = false;
 
   setDependence(computeDependence(this));
 }
@@ -2595,6 +2596,14 @@ static bool IsDecompositionDeclRefExpr(const Expr *E) {
   return isa_and_nonnull<DecompositionDecl>(Ref->getDecl());
 }
 
+bool Expr::initializesObjectWithDefaultMemberInit() const {
+  if (const auto *ILE = dyn_cast<InitListExpr>(this))
+    return ILE->InitListExprBits.InitializesObjectWithDefaultMemberInit;
+  if (const auto *PLIE = dyn_cast<CXXParenListInitExpr>(this))
+    return PLIE->InitializesObjectWithDefaultMemberInit;
+  return false;
+}
+
 bool Expr::isReadIfDiscardedInCPlusPlus11() const {
   // In C++11, discarded-value expressions of a certain form are special,
   // according to [expr]p10:
diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index 4cea437db4150..fe34232509c9e 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1567,6 +1567,16 @@ void JSONNodeDumper::VisitConstantExpr(const 
ConstantExpr *CE) {
 void JSONNodeDumper::VisitInitListExpr(const InitListExpr *ILE) {
   if (const FieldDecl *FD = ILE->getInitializedFieldInUnion())
     JOS.attribute("field", createBareDeclRef(FD));
+  attributeOnlyIfTrue("usesDefaultMemberInit",
+                      ILE->initializesObjectWithDefaultMemberInit());
+}
+
+void JSONNodeDumper::VisitCXXParenListInitExpr(
+    const CXXParenListInitExpr *PLIE) {
+  if (const FieldDecl *FD = PLIE->getInitializedFieldInUnion())
+    JOS.attribute("field", createBareDeclRef(FD));
+  attributeOnlyIfTrue("usesDefaultMemberInit",
+                      PLIE->initializesObjectWithDefaultMemberInit());
 }
 
 void JSONNodeDumper::VisitGenericSelectionExpr(
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index f10753fe675d8..0115c2d6441c8 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1745,6 +1745,18 @@ void TextNodeDumper::VisitInitListExpr(const 
InitListExpr *ILE) {
     dumpBareDeclRef(Field);
   }
   OS << ' ' << (ILE->isExplicit() ? "explicit" : "implicit");
+  if (ILE->initializesObjectWithDefaultMemberInit())
+    OS << " uses default member init";
+}
+
+void TextNodeDumper::VisitCXXParenListInitExpr(
+    const CXXParenListInitExpr *PLIE) {
+  if (auto *Field = PLIE->getInitializedFieldInUnion()) {
+    OS << " field ";
+    dumpBareDeclRef(Field);
+  }
+  if (PLIE->initializesObjectWithDefaultMemberInit())
+    OS << " uses default member init";
 }
 
 void TextNodeDumper::VisitGenericSelectionExpr(const GenericSelectionExpr *E) {
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 5263114ebca28..1f8b715ce7495 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -960,6 +960,16 @@ class CFGBuilder {
       B->appendScopeEnd(VD, S, cfg->getBumpVectorContext());
   }
 
+  /// Whether `E` establishes an object that `this` denotes inside the default
+  /// member initializers it uses, and those initializers are part of the CFG.
+  bool marksListInitObject(const Expr *E) {
+    assert((!BuildOpts.AddListInitObjectMarkers ||
+            BuildOpts.AddCXXDefaultInitExprInAggregates) &&
+           "markers are useless without the initializers they bracket");
+    return BuildOpts.AddListInitObjectMarkers &&
+           E->initializesObjectWithDefaultMemberInit();
+  }
+
   /// Find a relational comparison with an expression evaluating to a
   /// boolean and a constant other than 0 and 1.
   /// e.g. if ((x < y) == 10)
@@ -2612,9 +2622,28 @@ CFGBlock *CFGBuilder::VisitInitListExpr(InitListExpr 
*ILE, AddStmtChoice asc) {
       B = R;
     if (BuildOpts.AddCXXDefaultInitExprInAggregates) {
       if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Child))
-        if (Stmt *Child = DIE->getExpr())
-          if (CFGBlock *R = Visit(Child))
+        if (Stmt *Body = DIE->getExpr()) {
+          // Bracket the default member initializer -- and only it -- with the
+          // object this list-initialization establishes: `this` denotes that
+          // object there, whereas in the explicitly written elements it still
+          // denotes the object of the enclosing member function.
+          //
+          // The CFG is built backwards, so the end marker goes in first and
+          // the begin marker last; in execution order they bracket the body.
+          bool MarksObject = marksListInitObject(ILE);
+          if (MarksObject) {
+            autoCreateBlock();
+            Block->appendListInitObjectEnd(ILE, cfg->getBumpVectorContext());
+            B = Block;
+          }
+          if (CFGBlock *R = Visit(Body))
             B = R;
+          if (MarksObject) {
+            autoCreateBlock();
+            Block->appendListInitObjectBegin(ILE, cfg->getBumpVectorContext());
+            B = Block;
+          }
+        }
     }
   }
   return B;
@@ -5524,6 +5553,8 @@ CFGImplicitDtor::getDestructorDecl(ASTContext 
&astContext) const {
     case CFGElement::ScopeEnd:
     case CFGElement::FullExprCleanup:
     case CFGElement::CleanupFunction:
+    case CFGElement::ListInitObjectBegin:
+    case CFGElement::ListInitObjectEnd:
       llvm_unreachable("getDestructorDecl should only be used with "
                        "ImplicitDtors");
     case CFGElement::AutomaticObjectDtor: {
@@ -6127,6 +6158,16 @@ static void print_elem(raw_ostream &OS, 
StmtPrinterHelper &Helper,
     OS << ")";
     break;
 
+  case CFGElement::Kind::ListInitObjectBegin:
+  case CFGElement::Kind::ListInitObjectEnd:
+    OS << (E.getKind() == CFGElement::Kind::ListInitObjectBegin
+               ? "CFGListInitObjectBegin("
+               : "CFGListInitObjectEnd(");
+    E.castAs<CFGListInitObject>().getListInitExpr()->getType().print(
+        OS, PrintingPolicy(Helper.getLangOpts()));
+    OS << ")";
+    break;
+
   case CFGElement::Kind::NewAllocator:
     OS << "CFGNewAllocator(";
     if (const CXXNewExpr *AllocExpr = 
E.castAs<CFGNewAllocator>().getAllocatorExpr())
diff --git a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp 
b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
index 6f4c3e6531e9b..fcf572c30dab8 100644
--- a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
+++ b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
@@ -161,6 +161,11 @@ llvm::Expected<AdornedCFG> AdornedCFG::build(const Decl 
&D, Stmt &S,
   Options.AddTemporaryDtors = true;
   Options.AddInitializers = true;
   Options.AddCXXDefaultInitExprInCtors = true;
+  // Needed so that `this` inside a default member initializer used by a
+  // list-initialization is part of the CFG; the object it denotes is supplied
+  // by the surrounding CFGListInitObjectBegin/End markers.
+  Options.AddCXXDefaultInitExprInAggregates = true;
+  Options.AddListInitObjectMarkers = true;
   Options.AddLifetime = true;
   Options.AddParameterLifetimes = true;
 
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index ab0b76037837f..7829ec3f0557d 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -494,97 +494,6 @@ class ResultObjectVisitor : public AnalysisASTVisitor {
   DataflowAnalysisContext &DACtx;
 };
 
-/// A visitor that finds `CXXThisExpr` that can refer to an object other than
-/// the `this` of a member function.
-class ThisExprOverridesVisitor : public AnalysisASTVisitor {
-  using BaseVisitor = AnalysisASTVisitor;
-
-public:
-  ThisExprOverridesVisitor(
-      RecordStorageLocation *ThisPointeeLoc,
-      const llvm::DenseMap<const Expr *, RecordStorageLocation *>
-          &ResultObjectMap,
-      llvm::DenseMap<const CXXThisExpr *, RecordStorageLocation *>
-          &ThisExprOverrides)
-      : DefaultThisPointeeLoc(ThisPointeeLoc), 
ResultObjectMap(ResultObjectMap),
-        ThisExprOverrides(ThisExprOverrides) {
-    ThisLocations.push(DefaultThisPointeeLoc);
-  }
-
-  void traverseConstructorInits(const CXXConstructorDecl *Ctor) {
-    for (const CXXCtorInitializer *Init : Ctor->inits()) {
-      TraverseStmt(Init->getInit());
-    }
-  }
-
-  bool TraverseInitListExpr(InitListExpr *ILE) override {
-    if (!ILE->isSemanticForm() || ILE->isTransparent()) {
-      BaseVisitor::TraverseInitListExpr(ILE);
-      return true;
-    }
-    bool IsRecordType = ILE->getType()->isRecordType();
-    if (IsRecordType) {
-      auto It = ResultObjectMap.find(ILE);
-      if (It == ResultObjectMap.end()) {
-        llvm_unreachable("InitListExpr not found in ResultObjectMap");
-        return false;
-      }
-      InitListLocations.push(It->second);
-    }
-    BaseVisitor::TraverseInitListExpr(ILE);
-    if (IsRecordType)
-      InitListLocations.pop();
-    return true;
-  }
-
-  bool TraverseCXXParenListInitExpr(CXXParenListInitExpr *PLIE) override {
-    auto It = ResultObjectMap.find(PLIE);
-    if (It == ResultObjectMap.end()) {
-      llvm_unreachable("CXXParenListInitExpr not found in ResultObjectMap");
-      return false;
-    }
-    InitListLocations.push(It->second);
-    BaseVisitor::TraverseCXXParenListInitExpr(PLIE);
-    InitListLocations.pop();
-    return true;
-  }
-
-  bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *CDIE) override {
-    bool HasInitListLocations = !InitListLocations.empty();
-    if (HasInitListLocations) {
-      auto *Loc = InitListLocations.top();
-      ThisLocations.push(Loc);
-    }
-    BaseVisitor::TraverseCXXDefaultInitExpr(CDIE);
-    if (HasInitListLocations)
-      ThisLocations.pop();
-    return true;
-  }
-
-  bool TraverseCXXThisExpr(CXXThisExpr *This) override {
-    assert(!ThisLocations.empty());
-    auto *Loc = ThisLocations.top();
-    if (Loc != DefaultThisPointeeLoc)
-      ThisExprOverrides[This] = Loc;
-    return true;
-  }
-
-  // The default `this` pointee location (null if not in a member function).
-  RecordStorageLocation *DefaultThisPointeeLoc;
-  // Locations to use for `this`, with the most recent scope on top.
-  std::stack<RecordStorageLocation *> ThisLocations;
-  // A stack of nested InitListExpr and CXXParenListInitExprs storage
-  // locations that may be used for `this` if we enter a CXXDefaultInitExpr.
-  std::stack<RecordStorageLocation *> InitListLocations;
-  // Map to look up a storage location, e.g., when encountering an
-  // InitListExpr.
-  const llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap;
-  // The visitor will update this map with locations to use for `this`,
-  // if different from `DefaultThisPointeeLoc`.
-  llvm::DenseMap<const CXXThisExpr *, RecordStorageLocation *>
-      &ThisExprOverrides;
-};
-
 } // namespace
 
 void Environment::initialize() {
@@ -597,11 +506,6 @@ void Environment::initialize() {
         std::make_shared<PrValueToResultObject>(buildResultObjectMap(
             DACtx, InitialTargetStmt, getThisPointeeStorageLocation(),
             /*LocForRecordReturnValue=*/nullptr));
-
-    ThisExprOverrides =
-        std::make_shared<ThisExprOverridesMap>(buildThisExprOverridesMap(
-            InitialTargetStmt, getThisPointeeStorageLocation(),
-            *ResultObjectMap));
     return;
   }
 
@@ -665,11 +569,6 @@ void Environment::initialize() {
       std::make_shared<PrValueToResultObject>(buildResultObjectMap(
           DACtx, InitialTargetFunc, getThisPointeeStorageLocation(),
           LocForRecordReturnVal));
-
-  ThisExprOverrides =
-      std::make_shared<ThisExprOverridesMap>(buildThisExprOverridesMap(
-          InitialTargetFunc, getThisPointeeStorageLocation(),
-          *ResultObjectMap));
 }
 
 // FIXME: Add support for resetting globals after function calls to enable the
@@ -770,9 +669,6 @@ void Environment::pushCallInternal(const FunctionDecl 
*FuncDecl,
   ResultObjectMap = std::make_shared<PrValueToResultObject>(
       buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(),
                            LocForRecordReturnVal));
-  ThisExprOverrides =
-      std::make_shared<ThisExprOverridesMap>(buildThisExprOverridesMap(
-          FuncDecl, getThisPointeeStorageLocation(), *ResultObjectMap));
 }
 
 void Environment::popCall(const CallExpr *Call, const Environment &CalleeEnv) {
@@ -840,7 +736,6 @@ LatticeEffect Environment::widen(const Environment &PrevEnv,
   assert(ReturnLoc == PrevEnv.ReturnLoc);
   assert(LocForRecordReturnVal == PrevEnv.LocForRecordReturnVal);
   assert(ThisPointeeLoc == PrevEnv.ThisPointeeLoc);
-  assert(ThisExprOverrides == PrevEnv.ThisExprOverrides);
   assert(CallStack == PrevEnv.CallStack);
   assert(ResultObjectMap == PrevEnv.ResultObjectMap);
   assert(InitialTargetFunc == PrevEnv.InitialTargetFunc);
@@ -878,7 +773,6 @@ Environment Environment::join(const Environment &EnvA, 
const Environment &EnvB,
   assert(EnvA.DACtx == EnvB.DACtx);
   assert(EnvA.LocForRecordReturnVal == EnvB.LocForRecordReturnVal);
   assert(EnvA.ThisPointeeLoc == EnvB.ThisPointeeLoc);
-  assert(EnvA.ThisExprOverrides == EnvB.ThisExprOverrides);
   assert(EnvA.CallStack == EnvB.CallStack);
   assert(EnvA.ResultObjectMap == EnvB.ResultObjectMap);
   assert(EnvA.InitialTargetFunc == EnvB.InitialTargetFunc);
@@ -890,7 +784,10 @@ Environment Environment::join(const Environment &EnvA, 
const Environment &EnvB,
   JoinedEnv.ResultObjectMap = EnvA.ResultObjectMap;
   JoinedEnv.LocForRecordReturnVal = EnvA.LocForRecordReturnVal;
   JoinedEnv.ThisPointeeLoc = EnvA.ThisPointeeLoc;
-  JoinedEnv.ThisExprOverrides = EnvA.ThisExprOverrides;
+  // The markers that push and pop these follow the syntactic nesting of the
+  // list-initializations, so all paths reaching a join agree on them.
+  assert(EnvA.ListInitObjects == EnvB.ListInitObjects);
+  JoinedEnv.ListInitObjects = EnvA.ListInitObjects;
   JoinedEnv.InitialTargetFunc = EnvA.InitialTargetFunc;
   JoinedEnv.InitialTargetStmt = EnvA.InitialTargetStmt;
 
@@ -1342,30 +1239,6 @@ Environment::PrValueToResultObject 
Environment::buildResultObjectMap(
   return Map;
 }
 
-Environment::ThisExprOverridesMap Environment::buildThisExprOverridesMap(
-    const FunctionDecl *FuncDecl, RecordStorageLocation *ThisPointeeLoc,
-    const PrValueToResultObject &ResultObjectMap) {
-  assert(FuncDecl->doesThisDeclarationHaveABody());
-
-  ThisExprOverridesMap Map = buildThisExprOverridesMap(
-      FuncDecl->getBody(), ThisPointeeLoc, ResultObjectMap);
-
-  ThisExprOverridesVisitor Visitor(ThisPointeeLoc, ResultObjectMap, Map);
-  if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FuncDecl)) {
-    Visitor.traverseConstructorInits(Ctor);
-  }
-  return Map;
-}
-
-Environment::ThisExprOverridesMap Environment::buildThisExprOverridesMap(
-    Stmt *S, RecordStorageLocation *ThisPointeeLoc,
-    const PrValueToResultObject &ResultObjectMap) {
-  ThisExprOverridesMap Map;
-  ThisExprOverridesVisitor Visitor(ThisPointeeLoc, ResultObjectMap, Map);
-  Visitor.TraverseStmt(S);
-  return Map;
-}
-
 RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
                                                  const Environment &Env) {
   Expr *ImplicitObject = MCE.getImplicitObjectArgument();
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp 
b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index bbb40748c2d59..523afdcdaaa39 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -484,7 +484,13 @@ class TransferVisitor : public 
ConstStmtVisitor<TransferVisitor> {
   }
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
-    auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation(*S);
+    // Within a default member initializer used by a list-initialization,
+    // `this` denotes the object that initialization establishes; the CFG
+    // brackets it with markers that put it in the environment. Otherwise
+    // `this` is the pointee of the enclosing member function.
+    auto *ThisPointeeLoc = Env.getListInitObjectLocation();
+    if (ThisPointeeLoc == nullptr)
+      ThisPointeeLoc = Env.getThisPointeeStorageLocation();
     if (ThisPointeeLoc == nullptr)
       // Unions are not supported yet, and will not have a location for the
       // `this` expression's pointee.
diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp 
b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
index 9e0fd8122c890..bc432d025bb66 100644
--- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -398,6 +398,16 @@ static void builtinTransfer(unsigned CurBlockID, const 
CFGElement &Elt,
   case CFGElement::Initializer:
     builtinTransferInitializer(Elt.castAs<CFGInitializer>(), State);
     break;
+  case CFGElement::ListInitObjectBegin:
+    // Make the object this list-initialization establishes available while its
+    // elements -- including the default member initializers it uses -- are
+    // evaluated. See `Environment::getListInitObjectLocation()`.
+    State.Env.pushListInitObject(State.Env.getResultObjectLocation(
+        *Elt.castAs<CFGListInitObjectBegin>().getListInitExpr()));
+    break;
+  case CFGElement::ListInitObjectEnd:
+    State.Env.popListInitObject();
+    break;
   case CFGElement::LifetimeEnds:
     // Removing declarations when their lifetime ends serves two purposes:
     // - Eliminate unnecessary clutter from `Environment::DeclToLoc`
diff --git a/clang/lib/Analysis/PathDiagnostic.cpp 
b/clang/lib/Analysis/PathDiagnostic.cpp
index a95ef4582be99..c84dc61bf71ee 100644
--- a/clang/lib/Analysis/PathDiagnostic.cpp
+++ b/clang/lib/Analysis/PathDiagnostic.cpp
@@ -531,6 +531,10 @@ static PathDiagnosticLocation getLocationForCaller(const 
StackFrame *SF,
     return PathDiagnosticLocation(Init.getInitializer()->getInit(), SM,
                                   CallerSF);
   }
+  case CFGElement::ListInitObjectBegin:
+  case CFGElement::ListInitObjectEnd:
+    return PathDiagnosticLocation(
+        Source.castAs<CFGListInitObject>().getListInitExpr(), SM, CallerSF);
   case CFGElement::AutomaticObjectDtor: {
     const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>();
     return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), SM,
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 2b524a956ecc4..a4f37c82b5d97 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5754,9 +5754,11 @@ struct EnsureImmediateInvocationInDefaultArgs
   ExprResult TransformLambdaExpr(LambdaExpr *E) { return E; }
   ExprResult TransformBlockExpr(BlockExpr *E) { return E; }
 
-  // Make sure we don't rebuild the this pointer as it would
-  // cause it to incorrectly point it to the outermost class
-  // in the case of nested struct initialization.
+  // Copy the 'this' pointer rather than rebuilding it: rebuilding would
+  // re-derive its type from the current 'this' type, which would incorrectly
+  // point it to the outermost class in the case of nested struct
+  // initialization. Copying keeps the type and location, while still giving
+  // each use of a default member initializer its own node.
   ExprResult TransformCXXThisExpr(CXXThisExpr *E) { return E; }
 
   // Rewrite to source location to refer to the context in which they are used.
@@ -5927,11 +5929,23 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation 
Loc, FieldDecl *Field) {
   // expression is an ExprWithCleanups. Then make sure the normal lifetime
   // extension code recurses into the default initializer and does lifetime
   // extension when warranted.
+  //
+  // The initializer is rebuilt for *every* use, so that each
+  // CXXDefaultInitExpr owns its own copy of it. The initializer is written
+  // once but evaluated once per use, and `this` within it denotes whichever
+  // object that use initializes; sharing the nodes between uses would make
+  // the two indistinguishable to anything that keys state on `Expr *`.
+  //
+  // Only the rewrites above are semantic; the rest is a copy of an initializer
+  // that has already been checked where it was written. Re-checking it must
+  // therefore not diagnose a second time, and if re-checking fails to
+  // reproduce it we keep the one from the declaration instead of failing here.
   bool ContainsAnyTemporaries =
       isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
+  bool SemanticRebuild =
+      V.HasImmediateCalls || (NeedRebuild && ContainsAnyTemporaries);
   if (Field->getInClassInitializer() &&
-      !Field->getInClassInitializer()->containsErrors() &&
-      (V.HasImmediateCalls || (NeedRebuild && ContainsAnyTemporaries))) {
+      !Field->getInClassInitializer()->containsErrors() && SemanticRebuild) {
     ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
                                                                    CurContext};
     ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
@@ -5942,17 +5956,28 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation 
Loc, FieldDecl *Field) {
         parentEvaluationContext().InLifetimeExtendingContext;
     EnsureImmediateInvocationInDefaultArgs Immediate(*this);
     ExprResult Res;
-    runWithSufficientStackSpace(Loc, [&] {
-      Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
-                                           /*CXXDirectInit=*/false);
-    });
-    if (!Res.isInvalid())
-      Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
+    {
+      std::optional<TentativeAnalysisScope> SilenceRecheck;
+      if (!SemanticRebuild)
+        SilenceRecheck.emplace(*this);
+      runWithSufficientStackSpace(Loc, [&] {
+        Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
+                                             /*CXXDirectInit=*/false);
+      });
+      if (!Res.isInvalid())
+        Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
+    }
     if (Res.isInvalid()) {
-      Field->setInvalidDecl();
-      return ExprError();
+      if (SemanticRebuild) {
+        Field->setInvalidDecl();
+        return ExprError();
+      }
+      // Keep sharing the initializer from the declaration; it has already been
+      // checked and diagnosed there.
+      Init = nullptr;
+    } else {
+      Init = Res.get();
     }
-    Init = Res.get();
   }
 
   if (Field->getInClassInitializer()) {
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 48ce51863c2c0..db52b77d9989a 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -835,6 +835,9 @@ void InitListChecker::FillInEmptyInitForField(unsigned 
Init, FieldDecl *Field,
         return;
       }
       SemaRef.checkInitializerLifetime(MemberEntity, DIE.get());
+      // Record that `this` within the default member initializer denotes the
+      // object this list initializes.
+      ILE->setInitializesObjectWithDefaultMemberInit();
       if (Init < NumInits)
         ILE->setInit(Init, DIE.get());
       else {
@@ -5967,6 +5970,7 @@ static void TryOrBuildParenListInitialization(
   QualType ResultType;
   Expr *ArrayFiller = nullptr;
   FieldDecl *InitializedFieldInUnion = nullptr;
+  bool UsesDefaultMemberInit = false;
 
   auto HandleInitializedEntity = [&](const InitializedEntity &SubEntity,
                                      const InitializationKind &SubKind,
@@ -6158,6 +6162,7 @@ static void TryOrBuildParenListInitialization(
               return;
             S.checkInitializerLifetime(SubEntity, DIE.get());
             InitExprs.push_back(DIE.get());
+            UsesDefaultMemberInit = true;
           }
         } else {
           // C++ [dcl.init]p17.6.2.2
@@ -6211,6 +6216,8 @@ static void TryOrBuildParenListInitialization(
       CPLIE->setArrayFiller(ArrayFiller);
     if (InitializedFieldInUnion)
       CPLIE->setInitializedFieldInUnion(InitializedFieldInUnion);
+    if (UsesDefaultMemberInit)
+      CPLIE->setInitializesObjectWithDefaultMemberInit();
     *Result = CPLIE;
     S.Diag(Kind.getLocation(),
            diag::warn_cxx17_compat_aggregate_init_paren_list)
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index 92c555dc427b3..a60de794596ee 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1281,6 +1281,8 @@ void ASTStmtReader::VisitInitListExpr(InitListExpr *E) {
       E->updateInit(Record.getContext(), I, Record.readSubExpr());
   }
   E->InitListExprBits.IsExplicit = Record.readBool();
+  E->InitListExprBits.InitializesObjectWithDefaultMemberInit =
+      Record.readBool();
 }
 
 void ASTStmtReader::VisitDesignatedInitExpr(DesignatedInitExpr *E) {
@@ -2402,6 +2404,7 @@ void 
ASTStmtReader::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
       E->setInitializedFieldInUnion(readDeclAs<FieldDecl>());
     }
   }
+  E->InitializesObjectWithDefaultMemberInit = Record.readBool();
   E->updateDependence();
 }
 
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index 782fecdbd0c80..24fa8a2497277 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1251,6 +1251,7 @@ void ASTStmtWriter::VisitInitListExpr(InitListExpr *E) {
       Record.AddStmt(E->getInit(I));
   }
   Record.writeBool(E->isExplicit());
+  Record.writeBool(E->initializesObjectWithDefaultMemberInit());
   Code = serialization::EXPR_INIT_LIST;
 }
 
@@ -2418,6 +2419,7 @@ void 
ASTStmtWriter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
     else
       Record.AddDeclRef(UnionField);
   }
+  Record.writeBool(E->initializesObjectWithDefaultMemberInit());
   Code = serialization::EXPR_CXX_PAREN_LIST_INIT;
 }
 
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 530fae9ee2dee..3bedc55a4695d 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -976,6 +976,11 @@ void ExprEngine::processCFGElement(const CFGElement E, 
ExplodedNode *Pred,
     case CFGElement::LoopExit:
       ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred);
       return;
+    case CFGElement::ListInitObjectBegin:
+    case CFGElement::ListInitObjectEnd:
+      // These only carry information for consumers that need to know which
+      // object a default member initializer initializes; nothing to evaluate.
+      return;
     case CFGElement::LifetimeEnds:
       ProcessLifetimeEnd(E.castAs<CFGLifetimeEnds>().getTriggerStmt(),
                          E.castAs<CFGLifetimeEnds>().getVarDecl(), Pred);
diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp 
b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 1b217be452c6b..f7f0b54046bf7 100644
--- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -91,6 +91,9 @@ const Stmt *SymbolConjured::getStmt() const {
     return Elem->castAs<CFGScopeBegin>().getTriggerStmt();
   case CFGElement::ScopeEnd:
     return Elem->castAs<CFGScopeEnd>().getTriggerStmt();
+  case CFGElement::ListInitObjectBegin:
+  case CFGElement::ListInitObjectEnd:
+    return Elem->castAs<CFGListInitObject>().getListInitExpr();
   case CFGElement::NewAllocator:
     return Elem->castAs<CFGNewAllocator>().getAllocatorExpr();
   case CFGElement::LifetimeEnds:
diff --git a/clang/test/AST/ast-dump-uses-default-member-init.cpp 
b/clang/test/AST/ast-dump-uses-default-member-init.cpp
new file mode 100644
index 0000000000000..065ee13429b10
--- /dev/null
+++ b/clang/test/AST/ast-dump-uses-default-member-init.cpp
@@ -0,0 +1,52 @@
+// Test that a list-initialization which uses a default member initializer is
+// marked as such. Within such an initializer, `this` denotes the object the
+// list-initialization initializes, not the object of the enclosing member
+// function.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++20 -ast-dump %s \
+// RUN: | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++20 -ast-dump=json %s 
\
+// RUN: | FileCheck --check-prefix JSON %s
+
+struct WithDefault {
+  int x;
+  int *self = &x;
+};
+
+struct NoDefault {
+  int x;
+  int y;
+};
+
+void braces() {
+  WithDefault a{1};
+  NoDefault b{1, 2};
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} braces
+// CHECK:         InitListExpr {{.*}} 'WithDefault' explicit uses default 
member init
+// CHECK-NEXT:      IntegerLiteral {{.*}} 'int' 1
+// CHECK-NEXT:      CXXDefaultInitExpr
+// The marker is absent, so the line ends after `explicit`.
+// CHECK:         InitListExpr {{.*}} 'NoDefault' explicit{{$}}
+// CHECK-NEXT:      IntegerLiteral {{.*}} 'int' 1
+
+void parens() {
+  WithDefault a(1);
+  NoDefault b(1, 2);
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} parens
+// CHECK:         CXXParenListInitExpr {{.*}} 'WithDefault' uses default 
member init
+// CHECK-NEXT:      IntegerLiteral {{.*}} 'int' 1
+// CHECK-NEXT:      CXXDefaultInitExpr
+// CHECK:         CXXParenListInitExpr {{.*}} 'NoDefault'{{$}}
+// CHECK-NEXT:      IntegerLiteral {{.*}} 'int' 1
+
+// JSON:      "kind": "InitListExpr",
+// JSON:      "qualType": "WithDefault"
+// JSON:      "usesDefaultMemberInit": true
+
+// JSON:      "kind": "CXXParenListInitExpr",
+// JSON:      "qualType": "WithDefault"
+// JSON:      "usesDefaultMemberInit": true
diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
index 6bab26d472e66..b27df5c40eb4e 100644
--- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
@@ -525,251 +525,4 @@ TEST_F(EnvironmentTest, Stmt) {
 }
 
 // This is a crash repro.
-TEST_F(EnvironmentTest, LambdaCapturingThisInFieldInitializer) {
-  using namespace ast_matchers;
-  std::string Code = R"cc(
-      struct S {
-        int f{[this]() { return 1; }()};
-      };
-    )cc";
-
-  auto Unit =
-      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
-  auto &Context = Unit->getASTContext();
-
-  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
-
-  auto *LambdaCallOperator = selectFirst<CXXMethodDecl>(
-      "method", match(cxxMethodDecl(hasName("operator()"),
-                                    ofClass(cxxRecordDecl(isLambda())))
-                          .bind("method"),
-                      Context));
-
-  Environment Env(DAContext, *LambdaCallOperator);
-  // Don't crash when initializing.
-  Env.initialize();
-  // And initialize the captured `this` pointee.
-  ASSERT_NE(nullptr, Env.getThisPointeeStorageLocation());
-}
-
-TEST_F(EnvironmentTest, ThisExprLocInNonMemberIsInitListLoc) {
-  using namespace ast_matchers;
-  std::string Code = R"cc(
-      struct Other {
-        int i = 0;
-        int j = this->i;
-      };
-      void target(int x) {
-        Other o = {x};
-      }
-    )cc";
-
-  auto Unit =
-      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
-  auto &Context = Unit->getASTContext();
-
-  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
-
-  auto *Func = selectFirst<FunctionDecl>(
-      "func", match(functionDecl(hasName("target")).bind("func"), Context));
-  ASSERT_NE(Func, nullptr);
-
-  auto Results = match(
-      initListExpr(
-          hasInit(1, hasDescendant(memberExpr(
-                         hasObjectExpression(cxxThisExpr().bind("this_i")),
-                         member(fieldDecl(hasName("i")))))))
-          .bind("init_list"),
-      Context);
-
-  auto *ThisForI = selectFirst<CXXThisExpr>("this_i", Results);
-  ASSERT_NE(ThisForI, nullptr);
-  auto *InitList = selectFirst<InitListExpr>("init_list", Results);
-  ASSERT_NE(InitList, nullptr);
-
-  Environment Env(DAContext, *Func);
-  Env.initialize();
-  auto *DefaultThis = Env.getThisPointeeStorageLocation();
-  EXPECT_EQ(DefaultThis, nullptr);
-
-  RecordStorageLocation &InitListLoc = Env.getResultObjectLocation(*InitList);
-  EXPECT_EQ(&InitListLoc, Env.getThisPointeeStorageLocation(*ThisForI));
-}
-
-TEST_F(EnvironmentTest, ThisExprLocInNonMemberIsParenListInitLoc) {
-  using namespace ast_matchers;
-  std::string Code = R"cc(
-      struct Other {
-        int i = 0;
-        int j = this->i;
-      };
-      void target(int x) {
-        Other o(x);
-      }
-    )cc";
-
-  auto Unit =
-      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++20"});
-  auto &Context = Unit->getASTContext();
-
-  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
-
-  auto *Func = selectFirst<FunctionDecl>(
-      "func", match(functionDecl(hasName("target")).bind("func"), Context));
-  ASSERT_NE(Func, nullptr);
-
-  const ast_matchers::internal::VariadicDynCastAllOfMatcher<
-      Stmt, CXXParenListInitExpr>
-      cxxParenListInitExpr;
-
-  auto Results =
-      match(cxxParenListInitExpr(
-                hasDescendant(memberExpr(
-                    hasObjectExpression(cxxThisExpr().bind("this_i")),
-                    member(fieldDecl(hasName("i"))))))
-                .bind("init_list"),
-            Context);
-
-  auto *ThisForI = selectFirst<CXXThisExpr>("this_i", Results);
-  ASSERT_NE(ThisForI, nullptr);
-  auto *InitList = selectFirst<CXXParenListInitExpr>("init_list", Results);
-  ASSERT_NE(InitList, nullptr);
-
-  Environment Env(DAContext, *Func);
-  Env.initialize();
-  auto *DefaultThis = Env.getThisPointeeStorageLocation();
-  EXPECT_EQ(DefaultThis, nullptr);
-
-  RecordStorageLocation &InitListLoc = Env.getResultObjectLocation(*InitList);
-  EXPECT_EQ(&InitListLoc, Env.getThisPointeeStorageLocation(*ThisForI));
-}
-
-TEST_F(EnvironmentTest, ThisExprLocInMemberIsInitListLoc) {
-  using namespace ast_matchers;
-  std::string Code = R"cc(
-      struct Other {
-        int i = 0;
-        int j = this->i;
-      };
-      struct Foo {
-        void target() {
-          Other o = {this->x};
-        }
-        int x = 0;
-      };
-    )cc";
-
-  auto Unit =
-      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
-  auto &Context = Unit->getASTContext();
-
-  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
-
-  auto *Method = selectFirst<CXXMethodDecl>(
-      "method",
-      match(cxxMethodDecl(hasName("target")).bind("method"), Context));
-  ASSERT_NE(Method, nullptr);
-
-  auto Results = match(
-      initListExpr(
-          hasInit(0, hasDescendant(memberExpr(
-                         hasObjectExpression(cxxThisExpr().bind("this_x")),
-                         member(fieldDecl(hasName("x")))))),
-          hasInit(1, hasDescendant(memberExpr(
-                         hasObjectExpression(cxxThisExpr().bind("this_i")),
-                         member(fieldDecl(hasName("i")))))))
-          .bind("init_list"),
-      Context);
-
-  auto *ThisForX = selectFirst<CXXThisExpr>("this_x", Results);
-  ASSERT_NE(ThisForX, nullptr);
-  auto *ThisForI = selectFirst<CXXThisExpr>("this_i", Results);
-  ASSERT_NE(ThisForI, nullptr);
-  auto *InitList = selectFirst<InitListExpr>("init_list", Results);
-  ASSERT_NE(InitList, nullptr);
-
-  Environment Env(DAContext, *Method);
-  Env.initialize();
-  auto *DefaultThis = Env.getThisPointeeStorageLocation();
-  EXPECT_NE(DefaultThis, nullptr);
-
-  EXPECT_EQ(DefaultThis, Env.getThisPointeeStorageLocation(*ThisForX));
-  RecordStorageLocation &InitListLoc = Env.getResultObjectLocation(*InitList);
-  EXPECT_EQ(&InitListLoc, Env.getThisPointeeStorageLocation(*ThisForI));
-}
-
-TEST_F(EnvironmentTest, ThisExprLocInCtorInitializerIsInitListLoc) {
-  using namespace ast_matchers;
-  std::string Code = R"cc(
-      struct B {
-        int a = 0;
-        int b = this->a;
-      };
-      struct Other {
-        int i = 0;
-        B b = {this->i};
-      };
-      struct target {
-        target() : x(-1) {}
-        int x;
-        Other o = {this->x};
-      };
-    )cc";
-
-  auto Unit =
-      tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
-  auto &Context = Unit->getASTContext();
-
-  ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
-
-  auto *Ctor = selectFirst<CXXConstructorDecl>(
-      "ctor",
-      match(cxxConstructorDecl(hasName("target")).bind("ctor"), Context));
-  ASSERT_NE(Ctor, nullptr);
-  Ctor->dump();
-
-  auto Results = match(
-      initListExpr(
-          hasInit(0, hasDescendant(memberExpr(
-                         hasObjectExpression(cxxThisExpr().bind("this_x")),
-                         member(fieldDecl(hasName("x")))))),
-          hasInit(1, hasDescendant(
-                         initListExpr(
-                             hasInit(0, hasDescendant(memberExpr(
-                                            hasObjectExpression(
-                                                cxxThisExpr().bind("this_i")),
-                                            member(fieldDecl(hasName("i")))))),
-                             hasInit(1, hasDescendant(memberExpr(
-                                            hasObjectExpression(
-                                                cxxThisExpr().bind("this_a")),
-                                            member(fieldDecl(hasName("a")))))))
-                             .bind("init_list_inner"))))
-          .bind("init_list_outer"),
-      Context);
-
-  auto *ThisForX = selectFirst<CXXThisExpr>("this_x", Results);
-  ASSERT_NE(ThisForX, nullptr);
-  auto *ThisForI = selectFirst<CXXThisExpr>("this_i", Results);
-  ASSERT_NE(ThisForI, nullptr);
-  auto *ThisForA = selectFirst<CXXThisExpr>("this_a", Results);
-  ASSERT_NE(ThisForA, nullptr);
-  auto *InitListOuter = selectFirst<InitListExpr>("init_list_outer", Results);
-  ASSERT_NE(InitListOuter, nullptr);
-  auto *InitListInner = selectFirst<InitListExpr>("init_list_inner", Results);
-  ASSERT_NE(InitListInner, nullptr);
-
-  Environment Env(DAContext, *Ctor);
-  Env.initialize();
-  auto *DefaultThis = Env.getThisPointeeStorageLocation();
-  EXPECT_NE(DefaultThis, nullptr);
-
-  EXPECT_EQ(DefaultThis, Env.getThisPointeeStorageLocation(*ThisForX));
-  RecordStorageLocation &InitListOuterLoc =
-      Env.getResultObjectLocation(*InitListOuter);
-  EXPECT_EQ(&InitListOuterLoc, Env.getThisPointeeStorageLocation(*ThisForI));
-  RecordStorageLocation &InitListInnerLoc =
-      Env.getResultObjectLocation(*InitListInner);
-  EXPECT_EQ(&InitListInnerLoc, Env.getThisPointeeStorageLocation(*ThisForA));
-}
-
 } // namespace
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 60d1d038540ff..4bb154734e657 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2371,6 +2371,128 @@ TEST(TransferTest, DefaultInitializer) {
       });
 }
 
+TEST(TransferTest, DefaultInitializerInAggregateInit) {
+  // `this` inside the default member initializer for `Baz` denotes the object
+  // the list-initialization creates, not the enclosing function's object, so
+  // `Baz` binds to that object's `Bar`.
+  std::string Code = R"(
+    struct S {
+      int Bar;
+      int &Baz = this->Bar;
+    };
+    void target(int Foo) {
+      S Var = {Foo};
+      int &Qux = Var.Baz;
+      // [[p]]
+    }
+  )";
+  runDataflow(
+      Code,
+      [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+         ASTContext &ASTCtx) {
+        ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
+        const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
+
+        // `target` is not a member function, so there is no enclosing object.
+        EXPECT_THAT(Env.getThisPointeeStorageLocation(), IsNull());
+
+        const ValueDecl *VarDecl = findValueDecl(ASTCtx, "Var");
+        ASSERT_THAT(VarDecl, NotNull());
+        const auto *VarLoc =
+            cast<RecordStorageLocation>(Env.getStorageLocation(*VarDecl));
+        const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+        ASSERT_THAT(BarDecl, NotNull());
+
+        const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+        ASSERT_THAT(QuxDecl, NotNull());
+        EXPECT_EQ(Env.getStorageLocation(*QuxDecl),
+                  VarLoc->getChild(*cast<FieldDecl>(BarDecl)));
+      });
+}
+
+TEST(TransferTest, DefaultInitializerInAggregateInitInMemberFunction) {
+  // Here there *is* an enclosing object, and `this` inside the default member
+  // initializer must denote the list-initialized one rather than it.
+  std::string Code = R"(
+    struct S {
+      int Bar;
+      int &Baz = this->Bar;
+    };
+    struct Enclosing {
+      int Other;
+      void target(int Foo) {
+        S Var = {Foo};
+        int &Qux = Var.Baz;
+        // [[p]]
+      }
+    };
+  )";
+  runDataflow(
+      Code,
+      [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+         ASTContext &ASTCtx) {
+        ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
+        const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
+
+        const auto *ThisLoc = Env.getThisPointeeStorageLocation();
+        ASSERT_THAT(ThisLoc, NotNull());
+
+        const ValueDecl *VarDecl = findValueDecl(ASTCtx, "Var");
+        ASSERT_THAT(VarDecl, NotNull());
+        const auto *VarLoc =
+            cast<RecordStorageLocation>(Env.getStorageLocation(*VarDecl));
+
+        const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+        ASSERT_THAT(QuxDecl, NotNull());
+        const StorageLocation *QuxLoc = Env.getStorageLocation(*QuxDecl);
+
+        // `Qux` refers to `Var`'s `Bar`; the enclosing object is a different
+        // object entirely.
+        ASSERT_THAT(QuxLoc, NotNull());
+        EXPECT_NE(static_cast<const StorageLocation *>(ThisLoc),
+                  static_cast<const StorageLocation *>(VarLoc));
+        const auto *BarDecl = cast<FieldDecl>(findValueDecl(ASTCtx, "Bar"));
+        EXPECT_EQ(QuxLoc, VarLoc->getChild(*BarDecl));
+      });
+}
+
+TEST(TransferTest, DefaultInitializerInAggregateInitInCtorInitializer) {
+  // The list-initialization is itself a member initializer, so the enclosing
+  // object is the one under construction; `this` inside the default member
+  // initializer must still denote the list-initialized object.
+  std::string Code = R"(
+    struct NonTrivDtor { NonTrivDtor(int n); ~NonTrivDtor() {} };
+    struct S {
+      int Bar;
+      NonTrivDtor Tmp = NonTrivDtor(this->Bar);
+      int &Baz = this->Bar;
+    };
+    struct target {
+      S o_;
+      target(int f) : o_(S{f}) {
+        int &Qux = o_.Baz;
+        // [[p]]
+      }
+    };
+  )";
+  runDataflow(
+      Code,
+      [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+         ASTContext &ASTCtx) {
+        ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
+        const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
+        const auto *ThisLoc = Env.getThisPointeeStorageLocation();
+        ASSERT_THAT(ThisLoc, NotNull());
+        const auto *ODecl = cast<FieldDecl>(findValueDecl(ASTCtx, "o_"));
+        const auto *OLoc =
+            cast<RecordStorageLocation>(ThisLoc->getChild(*ODecl));
+        const auto *BarDecl = cast<FieldDecl>(findValueDecl(ASTCtx, "Bar"));
+        const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+        ASSERT_THAT(QuxDecl, NotNull());
+        EXPECT_EQ(Env.getStorageLocation(*QuxDecl), OLoc->getChild(*BarDecl));
+      });
+}
+
 TEST(TransferTest, DefaultInitializerReference) {
   std::string Code = R"(
     struct target {

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

Reply via email to