[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-03-01 Thread via cfe-commits

https://github.com/martinboehme closed 
https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-29 Thread via cfe-commits


@@ -2392,14 +2392,88 @@ TEST(TransferTest, InitListExprAsUnion) {
   } F;
 
  public:
-  constexpr target() : F{nullptr} {}
+  constexpr target() : F{nullptr} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
 };
   )cc";
   runDataflow(
   Code,
   [](const llvm::StringMap> ,
  ASTContext ) {
-// Just verify that it doesn't crash.
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+EXPECT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+  });
+}
+
+TEST(TransferTest, EmptyInitListExprForUnion) {
+  // This is a crash repro.
+  std::string Code = R"cc(
+class target {
+  union {
+int *a;
+bool *b;
+  } F;
+
+ public:
+  constexpr target() : F{} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
+};
+  )cc";
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> ,
+ ASTContext ) {
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));

martinboehme wrote:

I've added a comment explaining what the language rules are that govern the 
behavior we expect here.

https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-29 Thread via cfe-commits


@@ -2392,14 +2392,88 @@ TEST(TransferTest, InitListExprAsUnion) {
   } F;
 
  public:
-  constexpr target() : F{nullptr} {}
+  constexpr target() : F{nullptr} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
 };
   )cc";
   runDataflow(
   Code,
   [](const llvm::StringMap> ,
  ASTContext ) {
-// Just verify that it doesn't crash.
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+EXPECT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+  });
+}
+
+TEST(TransferTest, EmptyInitListExprForUnion) {
+  // This is a crash repro.
+  std::string Code = R"cc(
+class target {
+  union {
+int *a;
+bool *b;
+  } F;
+
+ public:
+  constexpr target() : F{} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
+};
+  )cc";
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> ,
+ ASTContext ) {
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+EXPECT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+  });
+}
+
+TEST(TransferTest, EmptyInitListExprForStruct) {
+  std::string Code = R"cc(
+class target {
+  struct {
+int *a;
+bool *b;
+  } F;
+
+ public:
+  constexpr target() : F{} {
+int *NullIntPtr = nullptr;
+bool *NullBoolPtr = nullptr;
+// [[p]]
+  }
+};
+  )cc";
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> ,
+ ASTContext ) {
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+ASSERT_EQ(AVal,

martinboehme wrote:

Good point -- these should have been `EXPECT_EQ`. Changed.

https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-29 Thread via cfe-commits

https://github.com/martinboehme updated 
https://github.com/llvm/llvm-project/pull/82986

>From cd64b0e04026235283016eaf1cd601076ab7aeb2 Mon Sep 17 00:00:00 2001
From: Martin Braenne 
Date: Tue, 27 Feb 2024 18:23:36 +
Subject: [PATCH 1/3] Reapply "[clang][dataflow] Correctly handle
 `InitListExpr` of union type." (#82856)

This reverts commit c4e94633e8a48ee33115d5d3161ee142fc1c9700.
---
 .../FlowSensitive/DataflowEnvironment.h   |  9 ---
 .../FlowSensitive/DataflowEnvironment.cpp | 18 ++---
 clang/lib/Analysis/FlowSensitive/Transfer.cpp | 25 +++
 .../Analysis/FlowSensitive/TestingSupport.h   | 19 ++
 .../Analysis/FlowSensitive/TransferTest.cpp   | 14 +--
 5 files changed, 65 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 7f8c70d169376e..62e7af7ac219bc 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -723,9 +723,12 @@ RecordStorageLocation *getImplicitObjectLocation(const 
CXXMemberCallExpr ,
 RecordStorageLocation *getBaseObjectLocation(const MemberExpr ,
  const Environment );
 
-/// Returns the fields of `RD` that are initialized by an `InitListExpr`, in 
the
-/// order in which they appear in `InitListExpr::inits()`.
-std::vector getFieldsForInitListExpr(const RecordDecl *RD);
+/// Returns the fields of a `RecordDecl` that are initialized by an
+/// `InitListExpr`, in the order in which they appear in
+/// `InitListExpr::inits()`.
+/// `Init->getType()` must be a record type.
+std::vector
+getFieldsForInitListExpr(const InitListExpr *InitList);
 
 /// Associates a new `RecordValue` with `Loc` and returns the new value.
 RecordValue (RecordStorageLocation , Environment );
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index d487944ce92111..0cfc26ea952cda 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -361,8 +361,8 @@ getFieldsGlobalsAndFuncs(const Stmt , FieldSet ,
 if (const auto *FD = dyn_cast(VD))
   Fields.insert(FD);
   } else if (auto *InitList = dyn_cast()) {
-if (RecordDecl *RD = InitList->getType()->getAsRecordDecl())
-  for (const auto *FD : getFieldsForInitListExpr(RD))
+if (InitList->getType()->isRecordType())
+  for (const auto *FD : getFieldsForInitListExpr(InitList))
 Fields.insert(FD);
   }
 }
@@ -1104,12 +1104,22 @@ RecordStorageLocation *getBaseObjectLocation(const 
MemberExpr ,
   return Env.get(*Base);
 }
 
-std::vector getFieldsForInitListExpr(const RecordDecl *RD) {
+std::vector
+getFieldsForInitListExpr(const InitListExpr *InitList) {
+  const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
+  assert(RD != nullptr);
+
+  std::vector Fields;
+
+  if (InitList->getType()->isUnionType()) {
+Fields.push_back(InitList->getInitializedFieldInUnion());
+return Fields;
+  }
+
   // Unnamed bitfields are only used for padding and do not appear in
   // `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
   // field list, and we thus need to remove them before mapping inits to
   // fields to avoid mapping inits to the wrongs fields.
-  std::vector Fields;
   llvm::copy_if(
   RD->fields(), std::back_inserter(Fields),
   [](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp 
b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 089854264f483a..3f7e2e27d25cfd 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -663,14 +663,7 @@ class TransferVisitor : public 
ConstStmtVisitor {
   void VisitInitListExpr(const InitListExpr *S) {
 QualType Type = S->getType();
 
-if (Type->isUnionType()) {
-  // FIXME: Initialize unions properly.
-  if (auto *Val = Env.createValue(Type))
-Env.setValue(*S, *Val);
-  return;
-}
-
-if (!Type->isStructureOrClassType()) {
+if (!Type->isRecordType()) {
   // Until array initialization is implemented, we skip arrays and don't
   // need to care about cases where `getNumInits() > 1`.
   if (!Type->isArrayType() && S->getNumInits() == 1)
@@ -688,10 +681,9 @@ class TransferVisitor : public 
ConstStmtVisitor {
 llvm::DenseMap FieldLocs;
 
 // This only contains the direct fields for the given type.
-std::vector FieldsForInit =
-getFieldsForInitListExpr(Type->getAsRecordDecl());
+std::vector FieldsForInit = getFieldsForInitListExpr(S);
 
-// `S->inits()` contains all the initializer epressions, including the
+// `S->inits()` contains all the initializer expressions, 

[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-29 Thread via cfe-commits


@@ -685,9 +685,22 @@ class TransferVisitor : public 
ConstStmtVisitor {
 
 // `S->inits()` contains all the initializer expressions, including the
 // ones for direct base classes.
-auto Inits = S->inits();
+ArrayRef Inits = S->inits();
 size_t InitIdx = 0;
 
+// Unions initialized with an empty initializer list need special 
treatment.

martinboehme wrote:

It appears that CodeGen also appears to special-case this like we do:

https://github.com/llvm/llvm-project/blob/e08fe575d5953b6ca0d7a578c1afa00247f0a12f/clang/lib/CodeGen/CGExprAgg.cpp#L1760

And if we changed the AST so that we had an `ImplicitValueInitExpr` here, it 
looks as if the general case in CodeGen would do the right thing.

I'll make a note of this for myself.

https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-27 Thread Yitzhak Mandelbaum via cfe-commits


@@ -2392,14 +2392,88 @@ TEST(TransferTest, InitListExprAsUnion) {
   } F;
 
  public:
-  constexpr target() : F{nullptr} {}
+  constexpr target() : F{nullptr} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
 };
   )cc";
   runDataflow(
   Code,
   [](const llvm::StringMap> ,
  ASTContext ) {
-// Just verify that it doesn't crash.
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+EXPECT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+  });
+}
+
+TEST(TransferTest, EmptyInitListExprForUnion) {
+  // This is a crash repro.
+  std::string Code = R"cc(
+class target {
+  union {
+int *a;
+bool *b;
+  } F;
+
+ public:
+  constexpr target() : F{} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
+};
+  )cc";
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> ,
+ ASTContext ) {
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+EXPECT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+  });
+}
+
+TEST(TransferTest, EmptyInitListExprForStruct) {
+  std::string Code = R"cc(
+class target {
+  struct {
+int *a;
+bool *b;
+  } F;
+
+ public:
+  constexpr target() : F{} {
+int *NullIntPtr = nullptr;
+bool *NullBoolPtr = nullptr;
+// [[p]]
+  }
+};
+  )cc";
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> ,
+ ASTContext ) {
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+ASSERT_EQ(AVal,

ymand wrote:

why ASSERT (vs EXPECT,  like above)?

https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-27 Thread Yitzhak Mandelbaum via cfe-commits


@@ -2392,14 +2392,88 @@ TEST(TransferTest, InitListExprAsUnion) {
   } F;
 
  public:
-  constexpr target() : F{nullptr} {}
+  constexpr target() : F{nullptr} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
 };
   )cc";
   runDataflow(
   Code,
   [](const llvm::StringMap> ,
  ASTContext ) {
-// Just verify that it doesn't crash.
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+EXPECT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+  });
+}
+
+TEST(TransferTest, EmptyInitListExprForUnion) {
+  // This is a crash repro.
+  std::string Code = R"cc(
+class target {
+  union {
+int *a;
+bool *b;
+  } F;
+
+ public:
+  constexpr target() : F{} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
+};
+  )cc";
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> ,
+ ASTContext ) {
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));

ymand wrote:

Why is `a` set when the initializer is empty? I'm guessing it's because line 
699 in Transfer.cpp uses `front()` and so initializes the first field of the 
union. If so, perhaps say this explicitly in the comment preceding those lines?

https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-27 Thread Yitzhak Mandelbaum via cfe-commits

https://github.com/ymand edited https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-27 Thread Yitzhak Mandelbaum via cfe-commits

https://github.com/ymand approved this pull request.


https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-27 Thread via cfe-commits

martinboehme wrote:

Just modified this PR to essentially reapply 
https://github.com/llvm/llvm-project/pull/82348 under it.

https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-27 Thread via cfe-commits

https://github.com/martinboehme updated 
https://github.com/llvm/llvm-project/pull/82986

>From cd64b0e04026235283016eaf1cd601076ab7aeb2 Mon Sep 17 00:00:00 2001
From: Martin Braenne 
Date: Tue, 27 Feb 2024 18:23:36 +
Subject: [PATCH 1/2] Reapply "[clang][dataflow] Correctly handle
 `InitListExpr` of union type." (#82856)

This reverts commit c4e94633e8a48ee33115d5d3161ee142fc1c9700.
---
 .../FlowSensitive/DataflowEnvironment.h   |  9 ---
 .../FlowSensitive/DataflowEnvironment.cpp | 18 ++---
 clang/lib/Analysis/FlowSensitive/Transfer.cpp | 25 +++
 .../Analysis/FlowSensitive/TestingSupport.h   | 19 ++
 .../Analysis/FlowSensitive/TransferTest.cpp   | 14 +--
 5 files changed, 65 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h 
b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 7f8c70d169376e..62e7af7ac219bc 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -723,9 +723,12 @@ RecordStorageLocation *getImplicitObjectLocation(const 
CXXMemberCallExpr ,
 RecordStorageLocation *getBaseObjectLocation(const MemberExpr ,
  const Environment );
 
-/// Returns the fields of `RD` that are initialized by an `InitListExpr`, in 
the
-/// order in which they appear in `InitListExpr::inits()`.
-std::vector getFieldsForInitListExpr(const RecordDecl *RD);
+/// Returns the fields of a `RecordDecl` that are initialized by an
+/// `InitListExpr`, in the order in which they appear in
+/// `InitListExpr::inits()`.
+/// `Init->getType()` must be a record type.
+std::vector
+getFieldsForInitListExpr(const InitListExpr *InitList);
 
 /// Associates a new `RecordValue` with `Loc` and returns the new value.
 RecordValue (RecordStorageLocation , Environment );
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index d487944ce92111..0cfc26ea952cda 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -361,8 +361,8 @@ getFieldsGlobalsAndFuncs(const Stmt , FieldSet ,
 if (const auto *FD = dyn_cast(VD))
   Fields.insert(FD);
   } else if (auto *InitList = dyn_cast()) {
-if (RecordDecl *RD = InitList->getType()->getAsRecordDecl())
-  for (const auto *FD : getFieldsForInitListExpr(RD))
+if (InitList->getType()->isRecordType())
+  for (const auto *FD : getFieldsForInitListExpr(InitList))
 Fields.insert(FD);
   }
 }
@@ -1104,12 +1104,22 @@ RecordStorageLocation *getBaseObjectLocation(const 
MemberExpr ,
   return Env.get(*Base);
 }
 
-std::vector getFieldsForInitListExpr(const RecordDecl *RD) {
+std::vector
+getFieldsForInitListExpr(const InitListExpr *InitList) {
+  const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
+  assert(RD != nullptr);
+
+  std::vector Fields;
+
+  if (InitList->getType()->isUnionType()) {
+Fields.push_back(InitList->getInitializedFieldInUnion());
+return Fields;
+  }
+
   // Unnamed bitfields are only used for padding and do not appear in
   // `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
   // field list, and we thus need to remove them before mapping inits to
   // fields to avoid mapping inits to the wrongs fields.
-  std::vector Fields;
   llvm::copy_if(
   RD->fields(), std::back_inserter(Fields),
   [](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp 
b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 089854264f483a..3f7e2e27d25cfd 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -663,14 +663,7 @@ class TransferVisitor : public 
ConstStmtVisitor {
   void VisitInitListExpr(const InitListExpr *S) {
 QualType Type = S->getType();
 
-if (Type->isUnionType()) {
-  // FIXME: Initialize unions properly.
-  if (auto *Val = Env.createValue(Type))
-Env.setValue(*S, *Val);
-  return;
-}
-
-if (!Type->isStructureOrClassType()) {
+if (!Type->isRecordType()) {
   // Until array initialization is implemented, we skip arrays and don't
   // need to care about cases where `getNumInits() > 1`.
   if (!Type->isArrayType() && S->getNumInits() == 1)
@@ -688,10 +681,9 @@ class TransferVisitor : public 
ConstStmtVisitor {
 llvm::DenseMap FieldLocs;
 
 // This only contains the direct fields for the given type.
-std::vector FieldsForInit =
-getFieldsForInitListExpr(Type->getAsRecordDecl());
+std::vector FieldsForInit = getFieldsForInitListExpr(S);
 
-// `S->inits()` contains all the initializer epressions, including the
+// `S->inits()` contains all the initializer expressions, 

[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-26 Thread Gábor Horváth via cfe-commits

https://github.com/Xazax-hun approved this pull request.


https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-26 Thread Gábor Horváth via cfe-commits


@@ -685,9 +685,22 @@ class TransferVisitor : public 
ConstStmtVisitor {
 
 // `S->inits()` contains all the initializer expressions, including the
 // ones for direct base classes.
-auto Inits = S->inits();
+ArrayRef Inits = S->inits();
 size_t InitIdx = 0;
 
+// Unions initialized with an empty initializer list need special 
treatment.

Xazax-hun wrote:

I am wondering how does CodeGen deal with this? If it happens to have similar 
extra logic, we could simplify both by changing the AST. 

https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-26 Thread Gábor Horváth via cfe-commits

https://github.com/Xazax-hun edited 
https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-26 Thread Yitzhak Mandelbaum via cfe-commits

https://github.com/ymand approved this pull request.


https://github.com/llvm/llvm-project/pull/82986
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-26 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang

Author: None (martinboehme)


Changes

This fixes a crash introduced by https://github.com/llvm/llvm-project/pull/82348
but also adds additional handling to make sure that we treat empty initializer
lists for both unions and structs/classes correctly (see tests added in this
patch).

---
Full diff: https://github.com/llvm/llvm-project/pull/82986.diff


3 Files Affected:

- (modified) clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp (+6-1) 
- (modified) clang/lib/Analysis/FlowSensitive/Transfer.cpp (+14-1) 
- (modified) clang/unittests/Analysis/FlowSensitive/TransferTest.cpp (+66-2) 


``diff
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 0cfc26ea952cda..fd7b06efcc7861 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -983,7 +983,7 @@ StorageLocation ::createObjectInternal(const 
ValueDecl *D,
   }
 
   Value *Val = nullptr;
-  if (InitExpr)
+  if (InitExpr) {
 // In the (few) cases where an expression is intentionally
 // "uninterpreted", `InitExpr` is not associated with a value.  There are
 // two ways to handle this situation: propagate the status, so that
@@ -998,6 +998,11 @@ StorageLocation ::createObjectInternal(const 
ValueDecl *D,
 // default value (assuming we don't update the environment API to return
 // references).
 Val = getValue(*InitExpr);
+
+if (!Val && isa(InitExpr) &&
+InitExpr->getType()->isPointerType())
+  Val = 
(InitExpr->getType()->getPointeeType());
+  }
   if (!Val)
 Val = createValue(Ty);
 
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp 
b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index cd1f04e53cff68..d73965806a533f 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -685,9 +685,22 @@ class TransferVisitor : public 
ConstStmtVisitor {
 
 // `S->inits()` contains all the initializer expressions, including the
 // ones for direct base classes.
-auto Inits = S->inits();
+ArrayRef Inits = S->inits();
 size_t InitIdx = 0;
 
+// Unions initialized with an empty initializer list need special 
treatment.
+// For structs/classes initialized with an empty initializer list, Clang
+// puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for 
unions,
+// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
+std::optional ImplicitValueInitForUnion;
+SmallVector InitsForUnion;
+if (S->getType()->isUnionType() && Inits.empty()) {
+  assert(FieldsForInit.size() == 1);
+  ImplicitValueInitForUnion.emplace(FieldsForInit.front()->getType());
+  InitsForUnion.push_back(&*ImplicitValueInitForUnion);
+  Inits = InitsForUnion;
+}
+
 // Initialize base classes.
 if (auto* R = S->getType()->getAsCXXRecordDecl()) {
   assert(FieldsForInit.size() + R->getNumBases() == Inits.size());
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index e7d74581865a32..2eb9a37f289426 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2393,8 +2393,72 @@ TEST(TransferTest, InitListExprAsUnion) {
 auto  = getFieldLoc(
 *Env.getThisPointeeStorageLocation(), "F", ASTCtx);
 auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
-ASSERT_EQ(AVal, (ASTCtx, Env, "null"));
-ASSERT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+EXPECT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+  });
+}
+
+TEST(TransferTest, EmptyInitListExprForUnion) {
+  // This is a crash repro.
+  std::string Code = R"cc(
+class target {
+  union {
+int *a;
+bool *b;
+  } F;
+
+ public:
+  constexpr target() : F{} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
+};
+  )cc";
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> ,
+ ASTContext ) {
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+EXPECT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+  });
+}
+
+TEST(TransferTest, EmptyInitListExprForStruct) {
+  std::string Code = R"cc(
+class target {
+  struct {
+int *a;
+bool *b;
+  } F;
+
+ public:
+  constexpr target() : F{} {
+int *NullIntPtr = nullptr;
+bool *NullBoolPtr = nullptr;
+// [[p]]
+  

[clang] [clang][dataflow] Correctly treat empty initializer lists for unions. (PR #82986)

2024-02-26 Thread via cfe-commits

https://github.com/martinboehme created 
https://github.com/llvm/llvm-project/pull/82986

This fixes a crash introduced by https://github.com/llvm/llvm-project/pull/82348
but also adds additional handling to make sure that we treat empty initializer
lists for both unions and structs/classes correctly (see tests added in this
patch).

>From a48dc2e9a481ba5ed5d801a59c1dbc23fe0092d1 Mon Sep 17 00:00:00 2001
From: Martin Braenne 
Date: Mon, 26 Feb 2024 11:41:08 +
Subject: [PATCH] [clang][dataflow] Correctly treat empty initializer lists for
 unions.

This fixes a crash introduced by https://github.com/llvm/llvm-project/pull/82348
but also adds additional handling to make sure that we treat empty initializer
lists for both unions and structs/classes correctly (see tests added in this
patch).
---
 .../FlowSensitive/DataflowEnvironment.cpp |  7 +-
 clang/lib/Analysis/FlowSensitive/Transfer.cpp | 15 +++-
 .../Analysis/FlowSensitive/TransferTest.cpp   | 68 ++-
 3 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp 
b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 0cfc26ea952cda..fd7b06efcc7861 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -983,7 +983,7 @@ StorageLocation ::createObjectInternal(const 
ValueDecl *D,
   }
 
   Value *Val = nullptr;
-  if (InitExpr)
+  if (InitExpr) {
 // In the (few) cases where an expression is intentionally
 // "uninterpreted", `InitExpr` is not associated with a value.  There are
 // two ways to handle this situation: propagate the status, so that
@@ -998,6 +998,11 @@ StorageLocation ::createObjectInternal(const 
ValueDecl *D,
 // default value (assuming we don't update the environment API to return
 // references).
 Val = getValue(*InitExpr);
+
+if (!Val && isa(InitExpr) &&
+InitExpr->getType()->isPointerType())
+  Val = 
(InitExpr->getType()->getPointeeType());
+  }
   if (!Val)
 Val = createValue(Ty);
 
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp 
b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index cd1f04e53cff68..d73965806a533f 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -685,9 +685,22 @@ class TransferVisitor : public 
ConstStmtVisitor {
 
 // `S->inits()` contains all the initializer expressions, including the
 // ones for direct base classes.
-auto Inits = S->inits();
+ArrayRef Inits = S->inits();
 size_t InitIdx = 0;
 
+// Unions initialized with an empty initializer list need special 
treatment.
+// For structs/classes initialized with an empty initializer list, Clang
+// puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for 
unions,
+// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
+std::optional ImplicitValueInitForUnion;
+SmallVector InitsForUnion;
+if (S->getType()->isUnionType() && Inits.empty()) {
+  assert(FieldsForInit.size() == 1);
+  ImplicitValueInitForUnion.emplace(FieldsForInit.front()->getType());
+  InitsForUnion.push_back(&*ImplicitValueInitForUnion);
+  Inits = InitsForUnion;
+}
+
 // Initialize base classes.
 if (auto* R = S->getType()->getAsCXXRecordDecl()) {
   assert(FieldsForInit.size() + R->getNumBases() == Inits.size());
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index e7d74581865a32..2eb9a37f289426 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2393,8 +2393,72 @@ TEST(TransferTest, InitListExprAsUnion) {
 auto  = getFieldLoc(
 *Env.getThisPointeeStorageLocation(), "F", ASTCtx);
 auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
-ASSERT_EQ(AVal, (ASTCtx, Env, "null"));
-ASSERT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+EXPECT_EQ(getFieldValue(, "b", ASTCtx, Env), nullptr);
+  });
+}
+
+TEST(TransferTest, EmptyInitListExprForUnion) {
+  // This is a crash repro.
+  std::string Code = R"cc(
+class target {
+  union {
+int *a;
+bool *b;
+  } F;
+
+ public:
+  constexpr target() : F{} {
+int *null = nullptr;
+F.b;  // Make sure we reference 'b' so it is modeled.
+// [[p]]
+  }
+};
+  )cc";
+  runDataflow(
+  Code,
+  [](const llvm::StringMap> ,
+ ASTContext ) {
+const Environment  = getEnvironmentAtAnnotation(Results, "p");
+
+auto  = getFieldLoc(
+*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
+auto *AVal = cast(getFieldValue(, "a", ASTCtx, 
Env));
+EXPECT_EQ(AVal, (ASTCtx, Env, "null"));
+