From 5e83a6e0f1daa755caa50c86d410d82c395beb6e Mon Sep 17 00:00:00 2001 From: Tom Roeder Date: Tue, 26 Feb 2019 19:26:41 +0000 Subject: [PATCH] [ASTImporter] Add support for importing ChooseExpr AST nodes. Summary: This allows ASTs to be merged when they contain ChooseExpr (the GNU __builtin_choose_expr construction). This is needed, for example, for cross-CTU analysis of C code that makes use of __builtin_choose_expr. 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. This was originally reviewed and approved in https://reviews.llvm.org/D58292 and submitted as r354832. It was reverted in r354839 due to failures on the Windows CI builds. This version fixes the test failures on Windows, which were caused by differences in template expansion between versions of clang on different OSes. The version of clang built with MSVC and running on Windows never expands the template in the C++ test in ImportExpr.ImportChooseExpr in clang/unittests/AST/ASTImporter.cpp, but the version on Linux does for the empty arguments and -fms-compatibility. So, this version of the patch drops the C++ test for __builtin_choose_expr, since that version was written to catch regressions of the logic for isConditionTrue() in the AST import code for ChooseExpr, and those regressions are also caught by ASTImporterOptionSpecificTestBase.ImportChooseExpr, which does work on Windows. Reviewers: shafik, a_sidorin, martong, aaron.ballman, rnk, a.sidorin Subscribers: cfe-commits, jdoerfert, rnkovacs, aaron.ballman Tags: #clang Differential Revision: https://reviews.llvm.org/D58663 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@354916 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LibASTMatchersReference.html | 5 +++ include/clang/ASTMatchers/ASTMatchers.h | 4 +++ lib/AST/ASTImporter.cpp | 28 +++++++++++++++ lib/ASTMatchers/ASTMatchersInternal.cpp | 1 + lib/ASTMatchers/Dynamic/Registry.cpp | 1 + test/ASTMerge/choose-expr/Inputs/choose.c | 2 ++ test/ASTMerge/choose-expr/test.c | 4 +++ unittests/AST/ASTImporterTest.cpp | 34 +++++++++++++++++++ unittests/ASTMatchers/ASTMatchersNodeTest.cpp | 5 +++ 9 files changed, 84 insertions(+) create mode 100644 test/ASTMerge/choose-expr/Inputs/choose.c create mode 100644 test/ASTMerge/choose-expr/test.c diff --git a/docs/LibASTMatchersReference.html b/docs/LibASTMatchersReference.html index 6506282f08..d2984844a4 100644 --- a/docs/LibASTMatchersReference.html +++ b/docs/LibASTMatchersReference.html @@ -788,6 +788,11 @@ Example matches 'a', L'a' +Matcher<Stmt>chooseExprMatcher<ChooseExpr>... +
Matches GNU __builtin_choose_expr.
+
+ + Matcher<Stmt>compoundLiteralExprMatcher<CompoundLiteralExpr>...
Matches compound (i.e. non-scalar) literals
 
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h
index 4c62ea0587..1f3e1f936d 100644
--- a/include/clang/ASTMatchers/ASTMatchers.h
+++ b/include/clang/ASTMatchers/ASTMatchers.h
@@ -2158,6 +2158,10 @@ extern const internal::VariadicDynCastAllOfMatcher
 extern const internal::VariadicDynCastAllOfMatcher
     cxxNullPtrLiteralExpr;
 
+/// Matches GNU __builtin_choose_expr.
+extern const internal::VariadicDynCastAllOfMatcher
+    chooseExpr;
+
 /// Matches GNU __null expression.
 extern const internal::VariadicDynCastAllOfMatcher
     gnuNullExpr;
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp
index 1034694115..27a39f3a78 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -552,6 +552,7 @@ namespace clang {
     // Importing expressions
     ExpectedStmt VisitExpr(Expr *E);
     ExpectedStmt VisitVAArgExpr(VAArgExpr *E);
+    ExpectedStmt VisitChooseExpr(ChooseExpr *E);
     ExpectedStmt VisitGNUNullExpr(GNUNullExpr *E);
     ExpectedStmt VisitPredefinedExpr(PredefinedExpr *E);
     ExpectedStmt VisitDeclRefExpr(DeclRefExpr *E);
@@ -6135,6 +6136,33 @@ ExpectedStmt ASTNodeImporter::VisitVAArgExpr(VAArgExpr *E) {
       E->isMicrosoftABI());
 }
 
+ExpectedStmt ASTNodeImporter::VisitChooseExpr(ChooseExpr *E) {
+  auto Imp = importSeq(E->getCond(), E->getLHS(), E->getRHS(),
+                       E->getBuiltinLoc(), E->getRParenLoc(), E->getType());
+  if (!Imp)
+    return Imp.takeError();
+
+  Expr *ToCond;
+  Expr *ToLHS;
+  Expr *ToRHS;
+  SourceLocation ToBuiltinLoc, ToRParenLoc;
+  QualType ToType;
+  std::tie(ToCond, ToLHS, ToRHS, ToBuiltinLoc, ToRParenLoc, ToType) = *Imp;
+
+  ExprValueKind VK = E->getValueKind();
+  ExprObjectKind OK = E->getObjectKind();
+
+  bool TypeDependent = ToCond->isTypeDependent();
+  bool ValueDependent = ToCond->isValueDependent();
+
+  // The value of CondIsTrue only matters if the value is not
+  // condition-dependent.
+  bool CondIsTrue = !E->isConditionDependent() && E->isConditionTrue();
+
+  return new (Importer.getToContext())
+      ChooseExpr(ToBuiltinLoc, ToCond, ToLHS, ToRHS, ToType, VK, OK,
+                 ToRParenLoc, CondIsTrue, TypeDependent, ValueDependent);
+}
 
 ExpectedStmt ASTNodeImporter::VisitGNUNullExpr(GNUNullExpr *E) {
   ExpectedType TypeOrErr = import(E->getType());
diff --git a/lib/ASTMatchers/ASTMatchersInternal.cpp b/lib/ASTMatchers/ASTMatchersInternal.cpp
index 8c5425680d..8c75b76156 100644
--- a/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -727,6 +727,7 @@ const internal::VariadicDynCastAllOfMatcher
     compoundLiteralExpr;
 const internal::VariadicDynCastAllOfMatcher
     cxxNullPtrLiteralExpr;
+const internal::VariadicDynCastAllOfMatcher chooseExpr;
 const internal::VariadicDynCastAllOfMatcher gnuNullExpr;
 const internal::VariadicDynCastAllOfMatcher atomicExpr;
 const internal::VariadicDynCastAllOfMatcher stmtExpr;
diff --git a/lib/ASTMatchers/Dynamic/Registry.cpp b/lib/ASTMatchers/Dynamic/Registry.cpp
index 8405cabcce..5a417c9dd6 100644
--- a/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -147,6 +147,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(caseStmt);
   REGISTER_MATCHER(castExpr);
   REGISTER_MATCHER(characterLiteral);
+  REGISTER_MATCHER(chooseExpr);
   REGISTER_MATCHER(classTemplateDecl);
   REGISTER_MATCHER(classTemplateSpecializationDecl);
   REGISTER_MATCHER(complexType);
diff --git a/test/ASTMerge/choose-expr/Inputs/choose.c b/test/ASTMerge/choose-expr/Inputs/choose.c
new file mode 100644
index 0000000000..0b413a6554
--- /dev/null
+++ b/test/ASTMerge/choose-expr/Inputs/choose.c
@@ -0,0 +1,2 @@
+_Static_assert(__builtin_choose_expr(1, 1, 0), "Incorrect semantics of __builtin_choose_expr");
+_Static_assert(__builtin_choose_expr(0, 0, 1), "Incorrect semantics of __builtin_choose_expr");
diff --git a/test/ASTMerge/choose-expr/test.c b/test/ASTMerge/choose-expr/test.c
new file mode 100644
index 0000000000..0f95cd5968
--- /dev/null
+++ b/test/ASTMerge/choose-expr/test.c
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -std=c11 -emit-pch -o %t.ast %S/Inputs/choose.c
+// RUN: %clang_cc1 -std=c11 -ast-merge %t.ast -fsyntax-only -verify %s
+// expected-no-diagnostics
+
diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp
index 5ab0370929..6bfb7ad065 100644
--- a/unittests/AST/ASTImporterTest.cpp
+++ b/unittests/AST/ASTImporterTest.cpp
@@ -563,6 +563,17 @@ TEST_P(ImportExpr, ImportStringLiteral) {
           stringLiteral(hasType(asString("const char [7]"))))));
 }
 
+TEST_P(ImportExpr, ImportChooseExpr) {
+  MatchVerifier Verifier;
+
+  // This case tests C code that is not condition-dependent and has a true
+  // condition.
+  testImport(
+    "void declToImport() { (void)__builtin_choose_expr(1, 2, 3); }",
+    Lang_C, "", Lang_C, Verifier,
+    functionDecl(hasDescendant(chooseExpr())));
+}
+
 TEST_P(ImportExpr, ImportGNUNullExpr) {
   MatchVerifier Verifier;
   testImport(
@@ -1312,6 +1323,29 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImportCorrectTemplatedDecl) {
   ASSERT_EQ(ToTemplated1, ToTemplated);
 }
 
+TEST_P(ASTImporterOptionSpecificTestBase, ImportChooseExpr) {
+  // This tests the import of isConditionTrue directly to make sure the importer
+  // gets it right.
+  Decl *From, *To;
+  std::tie(From, To) = getImportedDecl(
+    "void declToImport() { (void)__builtin_choose_expr(1, 0, 1); }",
+    Lang_C, "", Lang_C);
+
+  auto ToResults = match(chooseExpr().bind("choose"), To->getASTContext());
+  auto FromResults = match(chooseExpr().bind("choose"), From->getASTContext());
+
+  const ChooseExpr *FromChooseExpr =
+      selectFirst("choose", FromResults);
+  ASSERT_TRUE(FromChooseExpr);
+
+  const ChooseExpr *ToChooseExpr = selectFirst("choose", ToResults);
+  ASSERT_TRUE(ToChooseExpr);
+
+  EXPECT_EQ(FromChooseExpr->isConditionTrue(), ToChooseExpr->isConditionTrue());
+  EXPECT_EQ(FromChooseExpr->isConditionDependent(),
+            ToChooseExpr->isConditionDependent());
+}
+
 TEST_P(ASTImporterOptionSpecificTestBase,
        ImportFunctionWithBackReferringParameter) {
   Decl *From, *To;
diff --git a/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
index fd5b112fae..3f9981cd6d 100644
--- a/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -754,6 +754,11 @@ TEST(Matcher, NullPtrLiteral) {
   EXPECT_TRUE(matches("int* i = nullptr;", cxxNullPtrLiteralExpr()));
 }
 
+TEST(Matcher, ChooseExpr) {
+  EXPECT_TRUE(matchesC("void f() { (void)__builtin_choose_expr(1, 2, 3); }",
+                       chooseExpr()));
+}
+
 TEST(Matcher, GNUNullExpr) {
   EXPECT_TRUE(matches("int* i = __null;", gnuNullExpr()));
 }
-- 
2.40.0