Author: Jan Voung
Date: 2026-08-20T23:53:15+08:00
New Revision: 0d1770f6f3fe6f83f45038f7692d6b9e41cc6b7d

URL: 
https://github.com/llvm/llvm-project/commit/0d1770f6f3fe6f83f45038f7692d6b9e41cc6b7d
DIFF: 
https://github.com/llvm/llvm-project/commit/0d1770f6f3fe6f83f45038f7692d6b9e41cc6b7d.diff

LOG: [clang][dataflow] Handle when `this` refers to a different location 
(#146900)

When `this` is under a CXXDefaultInitExpr it could refer to the location
of an InitListExpr object instead of the `this` of a member function.
E.g.:
```
struct S {
  int x;
  int y = this->x;
};
struct R {
  int foo() {
    // `this` for `a` refers to an R, but `this`
    // for `x` refers to an S.
    return S{this->a}.y;
  }
  int a;
};
```
Prepares for https://github.com/llvm/llvm-project/issues/128068

Co-authored-by: Yihan Wang <[email protected]>

Added: 
    

Modified: 
    clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
    clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
    clang/lib/Analysis/FlowSensitive/Transfer.cpp
    clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
    clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 076714462bb2a..47ac06137bb5f 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -18,6 +18,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/Type.h"
 #include "clang/Analysis/FlowSensitive/ASTOps.h"
 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
@@ -359,10 +360,34 @@ class Environment {
   /// Returns the storage location assigned to the `this` pointee in the
   /// environment or null if the `this` pointee has no assigned storage 
location
   /// in the environment.
+  /// If you want to look up the storage location for a specific `CXXThisExpr`,
+  /// use the overload that takes a `CXXThisExpr`.
   RecordStorageLocation *getThisPointeeStorageLocation() const {
     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.:
+  ///
+  /// \code
+  ///   struct S {
+  ///     int x;
+  ///     int y = this->x;
+  ///   };
+  ///   int foo() {
+  ///     return S{10}.y;  // will have a `this` for initializing `S::y`.
+  ///   }
+  /// \endcode
+  RecordStorageLocation *
+  getThisPointeeStorageLocation(const CXXThisExpr &ThisExpr) const {
+    auto It = ThisExprOverrides->find(&ThisExpr);
+    if (It == ThisExprOverrides->end())
+      return ThisPointeeLoc;
+    return It->second;
+  }
+
   /// Sets the storage location assigned to the `this` pointee in the
   /// environment.
   void setThisPointeeStorageLocation(RecordStorageLocation &Loc) {
@@ -692,6 +717,8 @@ 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;
@@ -755,6 +782,15 @@ 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;
 
@@ -801,6 +837,11 @@ class Environment {
   // analysis target is not a method.
   RecordStorageLocation *ThisPointeeLoc = nullptr;
 
+  // Maps from `CXXThisExpr`s to their storage locations, if it should be
+  // 
diff erent 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/Analysis/FlowSensitive/DataflowEnvironment.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index bc13ee398fcad..ab0b76037837f 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -15,12 +15,14 @@
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/Type.h"
 #include "clang/Analysis/FlowSensitive/ASTOps.h"
 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
+#include "clang/Analysis/FlowSensitive/StorageLocation.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
@@ -30,6 +32,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include <cassert>
 #include <memory>
+#include <stack>
 #include <utility>
 
 #define DEBUG_TYPE "dataflow"
@@ -491,6 +494,97 @@ 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 
diff erent from `DefaultThisPointeeLoc`.
+  llvm::DenseMap<const CXXThisExpr *, RecordStorageLocation *>
+      &ThisExprOverrides;
+};
+
 } // namespace
 
 void Environment::initialize() {
@@ -503,6 +597,11 @@ void Environment::initialize() {
         std::make_shared<PrValueToResultObject>(buildResultObjectMap(
             DACtx, InitialTargetStmt, getThisPointeeStorageLocation(),
             /*LocForRecordReturnValue=*/nullptr));
+
+    ThisExprOverrides =
+        std::make_shared<ThisExprOverridesMap>(buildThisExprOverridesMap(
+            InitialTargetStmt, getThisPointeeStorageLocation(),
+            *ResultObjectMap));
     return;
   }
 
@@ -566,6 +665,11 @@ 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
@@ -666,6 +770,9 @@ 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) {
@@ -733,6 +840,7 @@ 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);
@@ -770,6 +878,7 @@ 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);
@@ -781,6 +890,7 @@ 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;
   JoinedEnv.InitialTargetFunc = EnvA.InitialTargetFunc;
   JoinedEnv.InitialTargetStmt = EnvA.InitialTargetStmt;
 
@@ -1232,6 +1342,30 @@ 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 bf4b245a3cf6f..bbb40748c2d59 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -484,7 +484,7 @@ class TransferVisitor : public 
ConstStmtVisitor<TransferVisitor> {
   }
 
   void VisitCXXThisExpr(const CXXThisExpr *S) {
-    auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
+    auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation(*S);
     if (ThisPointeeLoc == nullptr)
       // Unions are not supported yet, and will not have a location for the
       // `this` expression's pointee.

diff  --git 
a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
index 0780db921652c..6bab26d472e66 100644
--- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
@@ -8,7 +8,9 @@
 
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "TestingSupport.h"
+#include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -550,4 +552,224 @@ TEST_F(EnvironmentTest, 
LambdaCapturingThisInFieldInitializer) {
   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/UncheckedOptionalAccessModelTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
index 074dc95e2c388..f34568a072c32 100644
--- 
a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ 
b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -2929,6 +2929,27 @@ TEST_P(UncheckedOptionalAccessTest, 
DiagnosticsHaveRanges) {
   )cc");
 }
 
+TEST_P(UncheckedOptionalAccessTest, ConstructorOtherStructField) {
+  // Repro for a crash: https://github.com/llvm/llvm-project/issues/128068
+  ExpectDiagnosticsFor(R"cc(
+    #include "unchecked_optional_access_test.h"
+
+    struct NonTrivDtor {
+      NonTrivDtor(int n);
+      ~NonTrivDtor() {}
+    };
+    struct Other {
+      $ns::$optional<int> x;
+      NonTrivDtor b = NonTrivDtor(x.value());
+    };
+    struct target {
+      target(int f) : f_(f), o_(Other{f_}) {}
+      int f_;
+      Other o_;
+    };
+  )cc");
+}
+
 TEST_P(UncheckedOptionalAccessTest, AssertTrueGtestMacro) {
   ExpectDiagnosticsFor(R"cc(
     #include "unchecked_optional_access_test.h"


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

Reply via email to