[clang] 1844ab7 - [ASTImporter] Add support for importing GenericSelectionExpr AST nodes.

Tom Roeder via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 16 15:41:07 PST 2020


Author: Tom Roeder
Date: 2020-12-16T15:39:50-08:00
New Revision: 1844ab770cb9380a1896d83b1863b93766ffdf22

URL: https://github.com/llvm/llvm-project/commit/1844ab770cb9380a1896d83b1863b93766ffdf22
DIFF: https://github.com/llvm/llvm-project/commit/1844ab770cb9380a1896d83b1863b93766ffdf22.diff

LOG: [ASTImporter] Add support for importing GenericSelectionExpr AST nodes.

This allows ASTs to be merged when they contain GenericSelectionExpr
nodes (this is _Generic from C11). This is needed, for example, for
CTU analysis of C code that makes use of _Generic, like the Linux
kernel.

The node is already supported in the AST, but it didn't have a matcher
in ASTMatchers. So, this change adds the matcher and adds support to
ASTImporter. Additionally, this change adds support for structural
equivalence of _Generic in the AST.

Reviewed By: martong, aaron.ballman

Differential Revision: https://reviews.llvm.org/D92600

Added: 
    clang/test/ASTMerge/generic-selection-expr/Inputs/generic.c
    clang/test/ASTMerge/generic-selection-expr/Inputs/generic.cpp
    clang/test/ASTMerge/generic-selection-expr/test.c
    clang/test/ASTMerge/generic-selection-expr/test.cpp

Modified: 
    clang/docs/LibASTMatchersReference.html
    clang/include/clang/ASTMatchers/ASTMatchers.h
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/ASTStructuralEquivalence.cpp
    clang/lib/ASTMatchers/ASTMatchersInternal.cpp
    clang/lib/ASTMatchers/Dynamic/Registry.cpp
    clang/lib/Analysis/ExprMutationAnalyzer.cpp
    clang/unittests/AST/ASTImporterTest.cpp
    clang/unittests/AST/StructuralEquivalenceTest.cpp
    clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 203840c214a2..aaaa18acfc48 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -1704,6 +1704,11 @@ <h2 id="decl-matchers">Node Matchers</h2>
 </pre></td></tr>
 
 
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('genericSelectionExpr0')"><a name="genericSelectionExpr0Anchor">genericSelectionExpr</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1GenericSelectionExpr.html">GenericSelectionExpr</a>>...</td></tr>
+<tr><td colspan="4" class="doc" id="genericSelectionExpr0"><pre>Matches C11 _Generic expression.
+</pre></td></tr>
+
+
 <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('gnuNullExpr0')"><a name="gnuNullExpr0Anchor">gnuNullExpr</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1GNUNullExpr.html">GNUNullExpr</a>>...</td></tr>
 <tr><td colspan="4" class="doc" id="gnuNullExpr0"><pre>Matches GNU __null expression.
 </pre></td></tr>

diff  --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 0da469ea0f78..ba2dd862f171 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2362,6 +2362,10 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, ChooseExpr>
 extern const internal::VariadicDynCastAllOfMatcher<Stmt, GNUNullExpr>
     gnuNullExpr;
 
+/// Matches C11 _Generic expression.
+extern const internal::VariadicDynCastAllOfMatcher<Stmt, GenericSelectionExpr>
+    genericSelectionExpr;
+
 /// Matches atomic builtins.
 /// Example matches __atomic_load_n(ptr, 1)
 /// \code

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 9374e9316ce8..54816b721a4a 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -577,6 +577,7 @@ namespace clang {
     ExpectedStmt VisitVAArgExpr(VAArgExpr *E);
     ExpectedStmt VisitChooseExpr(ChooseExpr *E);
     ExpectedStmt VisitGNUNullExpr(GNUNullExpr *E);
+    ExpectedStmt VisitGenericSelectionExpr(GenericSelectionExpr *E);
     ExpectedStmt VisitPredefinedExpr(PredefinedExpr *E);
     ExpectedStmt VisitDeclRefExpr(DeclRefExpr *E);
     ExpectedStmt VisitImplicitValueInitExpr(ImplicitValueInitExpr *E);
@@ -6526,6 +6527,40 @@ ExpectedStmt ASTNodeImporter::VisitGNUNullExpr(GNUNullExpr *E) {
   return new (Importer.getToContext()) GNUNullExpr(*TypeOrErr, *BeginLocOrErr);
 }
 
+ExpectedStmt
+ASTNodeImporter::VisitGenericSelectionExpr(GenericSelectionExpr *E) {
+  Error Err = Error::success();
+  auto ToGenericLoc = importChecked(Err, E->getGenericLoc());
+  auto *ToControllingExpr = importChecked(Err, E->getControllingExpr());
+  auto ToDefaultLoc = importChecked(Err, E->getDefaultLoc());
+  auto ToRParenLoc = importChecked(Err, E->getRParenLoc());
+  if (Err)
+    return std::move(Err);
+
+  ArrayRef<const TypeSourceInfo *> FromAssocTypes(E->getAssocTypeSourceInfos());
+  SmallVector<TypeSourceInfo *, 1> ToAssocTypes(FromAssocTypes.size());
+  if (Error Err = ImportContainerChecked(FromAssocTypes, ToAssocTypes))
+    return std::move(Err);
+
+  ArrayRef<const Expr *> FromAssocExprs(E->getAssocExprs());
+  SmallVector<Expr *, 1> ToAssocExprs(FromAssocExprs.size());
+  if (Error Err = ImportContainerChecked(FromAssocExprs, ToAssocExprs))
+    return std::move(Err);
+
+  const ASTContext &ToCtx = Importer.getToContext();
+  if (E->isResultDependent()) {
+    return GenericSelectionExpr::Create(
+        ToCtx, ToGenericLoc, ToControllingExpr,
+        llvm::makeArrayRef(ToAssocTypes), llvm::makeArrayRef(ToAssocExprs),
+        ToDefaultLoc, ToRParenLoc, E->containsUnexpandedParameterPack());
+  }
+
+  return GenericSelectionExpr::Create(
+      ToCtx, ToGenericLoc, ToControllingExpr, llvm::makeArrayRef(ToAssocTypes),
+      llvm::makeArrayRef(ToAssocExprs), ToDefaultLoc, ToRParenLoc,
+      E->containsUnexpandedParameterPack(), E->getResultIndex());
+}
+
 ExpectedStmt ASTNodeImporter::VisitPredefinedExpr(PredefinedExpr *E) {
 
   Error Err = Error::success();

diff  --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 2bc5f39b817e..d004e443ae06 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -233,6 +233,24 @@ class StmtComparer {
     return E1->isExact() == E2->isExact() && E1->getValue() == E2->getValue();
   }
 
+  bool IsStmtEquivalent(const GenericSelectionExpr *E1,
+                        const GenericSelectionExpr *E2) {
+    for (auto Pair : zip_longest(E1->getAssocTypeSourceInfos(),
+                                 E2->getAssocTypeSourceInfos())) {
+      Optional<TypeSourceInfo *> Child1 = std::get<0>(Pair);
+      Optional<TypeSourceInfo *> Child2 = std::get<1>(Pair);
+      // Skip this case if there are a 
diff erent number of associated types.
+      if (!Child1 || !Child2)
+        return false;
+
+      if (!IsStructurallyEquivalent(Context, (*Child1)->getType(),
+                                    (*Child2)->getType()))
+        return false;
+    }
+
+    return true;
+  }
+
   bool IsStmtEquivalent(const ImplicitCastExpr *CastE1,
                         const ImplicitCastExpr *CastE2) {
     return IsStructurallyEquivalent(Context, CastE1->getType(),

diff  --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 4cc374c86d00..3c19bceb079e 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -905,6 +905,8 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, CXXNullPtrLiteralExpr>
     cxxNullPtrLiteralExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, ChooseExpr> chooseExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, GNUNullExpr> gnuNullExpr;
+const internal::VariadicDynCastAllOfMatcher<Stmt, GenericSelectionExpr>
+    genericSelectionExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, AtomicExpr> atomicExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, StmtExpr> stmtExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, BinaryOperator>

diff  --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 87a54d308a50..390d883dc5e3 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -239,6 +239,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(functionProtoType);
   REGISTER_MATCHER(functionTemplateDecl);
   REGISTER_MATCHER(functionType);
+  REGISTER_MATCHER(genericSelectionExpr);
   REGISTER_MATCHER(gnuNullExpr);
   REGISTER_MATCHER(gotoStmt);
   REGISTER_MATCHER(has);

diff  --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index d129c2d480f0..e9ff5e5e8765 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -95,10 +95,6 @@ AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
   return Node.isPotentiallyEvaluated();
 }
 
-const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
-                                                          GenericSelectionExpr>
-    genericSelectionExpr;
-
 AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
   return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);

diff  --git a/clang/test/ASTMerge/generic-selection-expr/Inputs/generic.c b/clang/test/ASTMerge/generic-selection-expr/Inputs/generic.c
new file mode 100644
index 000000000000..41b8d1705811
--- /dev/null
+++ b/clang/test/ASTMerge/generic-selection-expr/Inputs/generic.c
@@ -0,0 +1,6 @@
+void f() {
+  int x;
+  float y;
+  _Static_assert(_Generic(x, float : 0, int : 1), "Incorrect semantics of _Generic");
+  _Static_assert(_Generic(y, float : 1, int : 0), "Incorrect semantics of _Generic");
+}

diff  --git a/clang/test/ASTMerge/generic-selection-expr/Inputs/generic.cpp b/clang/test/ASTMerge/generic-selection-expr/Inputs/generic.cpp
new file mode 100644
index 000000000000..8a97338ee7cb
--- /dev/null
+++ b/clang/test/ASTMerge/generic-selection-expr/Inputs/generic.cpp
@@ -0,0 +1,5 @@
+template <typename T>
+void f() {
+  T x;
+  _Static_assert(_Generic(x, float : 0, int : 1), "Incorrect semantics of _Generic");
+}

diff  --git a/clang/test/ASTMerge/generic-selection-expr/test.c b/clang/test/ASTMerge/generic-selection-expr/test.c
new file mode 100644
index 000000000000..b5adee7af0eb
--- /dev/null
+++ b/clang/test/ASTMerge/generic-selection-expr/test.c
@@ -0,0 +1,3 @@
+// RUN: %clang_cc1 -std=c11 -emit-pch -o %t.ast %S/Inputs/generic.c
+// RUN: %clang_cc1 -std=c11 -ast-merge %t.ast -fsyntax-only -verify %s
+// expected-no-diagnostics

diff  --git a/clang/test/ASTMerge/generic-selection-expr/test.cpp b/clang/test/ASTMerge/generic-selection-expr/test.cpp
new file mode 100644
index 000000000000..a1874a4c95c8
--- /dev/null
+++ b/clang/test/ASTMerge/generic-selection-expr/test.cpp
@@ -0,0 +1,3 @@
+// RUN: %clang_cc1 -std=c++03 -emit-pch -o %t.ast %S/Inputs/generic.cpp
+// RUN: %clang_cc1 -std=c++03 -ast-merge %t.ast -fsyntax-only -verify %s
+// expected-no-diagnostics

diff  --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index de0705d3019b..2472fa265589 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -279,6 +279,15 @@ TEST_P(ImportExpr, ImportGNUNullExpr) {
              functionDecl(hasDescendant(gnuNullExpr(hasType(isInteger())))));
 }
 
+TEST_P(ImportExpr, ImportGenericSelectionExpr) {
+  MatchVerifier<Decl> Verifier;
+
+  testImport(
+      "void declToImport() { int x; (void)_Generic(x, int: 0, float: 1); }",
+      Lang_C99, "", Lang_C99, Verifier,
+      functionDecl(hasDescendant(genericSelectionExpr())));
+}
+
 TEST_P(ImportExpr, ImportCXXNullPtrLiteralExpr) {
   MatchVerifier<Decl> Verifier;
   testImport(
@@ -1087,6 +1096,36 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImportChooseExpr) {
             ToChooseExpr->isConditionDependent());
 }
 
+TEST_P(ASTImporterOptionSpecificTestBase, ImportGenericSelectionExpr) {
+  Decl *From, *To;
+  std::tie(From, To) = getImportedDecl(
+      R"(
+      int declToImport() {
+        int x;
+        return _Generic(x, int: 0, default: 1);
+      }
+      )",
+      Lang_C99, "", Lang_C99);
+
+  auto ToResults =
+      match(genericSelectionExpr().bind("expr"), To->getASTContext());
+  auto FromResults =
+      match(genericSelectionExpr().bind("expr"), From->getASTContext());
+
+  const GenericSelectionExpr *FromGenericSelectionExpr =
+      selectFirst<GenericSelectionExpr>("expr", FromResults);
+  ASSERT_TRUE(FromGenericSelectionExpr);
+
+  const GenericSelectionExpr *ToGenericSelectionExpr =
+      selectFirst<GenericSelectionExpr>("expr", ToResults);
+  ASSERT_TRUE(ToGenericSelectionExpr);
+
+  EXPECT_EQ(FromGenericSelectionExpr->isResultDependent(),
+            ToGenericSelectionExpr->isResultDependent());
+  EXPECT_EQ(FromGenericSelectionExpr->getResultIndex(),
+            ToGenericSelectionExpr->getResultIndex());
+}
+
 TEST_P(ASTImporterOptionSpecificTestBase,
        ImportFunctionWithBackReferringParameter) {
   Decl *From, *To;

diff  --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp
index 26bd81ce1319..d416e9d30127 100644
--- a/clang/unittests/AST/StructuralEquivalenceTest.cpp
+++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp
@@ -1598,6 +1598,72 @@ TEST_F(StructuralEquivalenceStmtTest, FloatingLiteralDifferentValue) {
   EXPECT_FALSE(testStructuralMatch(t));
 }
 
+TEST_F(StructuralEquivalenceStmtTest, GenericSelectionExprSame) {
+  auto t = makeWrappedStmts("_Generic(0u, unsigned int: 0, float: 1)",
+                            "_Generic(0u, unsigned int: 0, float: 1)", Lang_C99,
+                            genericSelectionExpr());
+  EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceStmtTest, GenericSelectionExprSignsDiffer) {
+  auto t = makeWrappedStmts("_Generic(0u, unsigned int: 0, float: 1)",
+                            "_Generic(0, int: 0, float: 1)", Lang_C99,
+                            genericSelectionExpr());
+  EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceStmtTest, GenericSelectionExprOrderDiffers) {
+  auto t = makeWrappedStmts("_Generic(0u, unsigned int: 0, float: 1)",
+                            "_Generic(0u, float: 1, unsigned int: 0)", Lang_C99,
+                            genericSelectionExpr());
+  EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceStmtTest, GenericSelectionExprDependentResultSame) {
+  auto t = makeStmts(
+      R"(
+      template <typename T>
+      void f() {
+        T x;
+        (void)_Generic(x, int: 0, float: 1);
+      }
+      void g() { f<int>(); }
+      )",
+      R"(
+      template <typename T>
+      void f() {
+        T x;
+        (void)_Generic(x, int: 0, float: 1);
+      }
+      void g() { f<int>(); }
+      )",
+      Lang_CXX03, genericSelectionExpr());
+  EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceStmtTest,
+       GenericSelectionExprDependentResultOrderDiffers) {
+  auto t = makeStmts(
+      R"(
+      template <typename T>
+      void f() {
+        T x;
+        (void)_Generic(x, float: 1, int: 0);
+      }
+      void g() { f<int>(); }
+      )",
+      R"(
+      template <typename T>
+      void f() {
+        T x;
+        (void)_Generic(x, int: 0, float: 1);
+      }
+      void g() { f<int>(); }
+      )",
+      Lang_CXX03, genericSelectionExpr());
+
+  EXPECT_FALSE(testStructuralMatch(t));
+}
 TEST_F(StructuralEquivalenceStmtTest, IntegerLiteral) {
   auto t = makeWrappedStmts("1", "1", Lang_CXX03, integerLiteral());
   EXPECT_TRUE(testStructuralMatch(t));

diff  --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
index 7ce17ce95621..edd9e2290509 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -982,6 +982,11 @@ TEST_P(ASTMatchersTest, GNUNullExpr) {
   EXPECT_TRUE(matches("int* i = __null;", gnuNullExpr()));
 }
 
+TEST_P(ASTMatchersTest, GenericSelectionExpr) {
+  EXPECT_TRUE(matches("void f() { (void)_Generic(1, int: 1, float: 2.0); }",
+                      genericSelectionExpr()));
+}
+
 TEST_P(ASTMatchersTest, AtomicExpr) {
   EXPECT_TRUE(matches("void foo() { int *ptr; __atomic_load_n(ptr, 1); }",
                       atomicExpr()));


        


More information about the cfe-commits mailing list