tmroeder updated this revision to Diff 311706. tmroeder added a comment. Change the DeclMatcher.h test code to expose the ability to not assert on null Nodes from a match.
This allows the tests for GenericSelectionExpr to handle the null case on Windows themselves without crashing, and to still work correctly and test the appropriate functionality on Linux. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D92600/new/ https://reviews.llvm.org/D92600 Files: 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/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 clang/unittests/AST/ASTImporterTest.cpp clang/unittests/AST/DeclMatcher.h clang/unittests/AST/StructuralEquivalenceTest.cpp clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
Index: clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -985,6 +985,11 @@ 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())); Index: clang/unittests/AST/StructuralEquivalenceTest.cpp =================================================================== --- clang/unittests/AST/StructuralEquivalenceTest.cpp +++ clang/unittests/AST/StructuralEquivalenceTest.cpp @@ -92,23 +92,34 @@ // Get a pair of node pointers into the synthesized AST from the given code // snippets. To determine the returned node, a separate matcher is specified - // for both snippets. The first matching node is returned. + // for both snippets. The first matching node is returned. AssertNonNull + // controls whether or not to assert that the return nodes are not null. template <typename MatcherType> std::tuple<StmtWithASTContext, StmtWithASTContext> makeStmts(const std::string &SrcCode0, const std::string &SrcCode1, TestLanguage Lang, const MatcherType &Matcher0, - const MatcherType &Matcher1) { + const MatcherType &Matcher1, bool AssertNonNull) { makeASTUnits(SrcCode0, SrcCode1, Lang); Stmt *S0 = FirstDeclMatcher<Stmt>().match( - AST0->getASTContext().getTranslationUnitDecl(), Matcher0); + AST0->getASTContext().getTranslationUnitDecl(), Matcher0, + AssertNonNull); Stmt *S1 = FirstDeclMatcher<Stmt>().match( - AST1->getASTContext().getTranslationUnitDecl(), Matcher1); + AST1->getASTContext().getTranslationUnitDecl(), Matcher1, + AssertNonNull); return std::make_tuple(StmtWithASTContext(*S0, AST0->getASTContext()), StmtWithASTContext(*S1, AST1->getASTContext())); } + template <typename MatcherType> + std::tuple<StmtWithASTContext, StmtWithASTContext> + makeStmts(const std::string &SrcCode0, const std::string &SrcCode1, + TestLanguage Lang, const MatcherType &Matcher0, + const MatcherType &Matcher1) { + return makeStmts(SrcCode0, SrcCode1, Lang, Matcher0, Matcher1, true); + } + // Get a pair of node pointers into the synthesized AST from the given code // snippets. The same matcher is used for both snippets. template <typename MatcherType> @@ -1598,6 +1609,73 @@ 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); + } + )", + R"( + template <typename T> + void f() { + T x; + (void)_Generic(x, int: 0, float: 1); + } + )", + Lang_CXX03, genericSelectionExpr(), genericSelectionExpr(), false); + 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); + } + )", + R"( + template <typename T> + void f() { + T x; + (void)_Generic(x, int: 0, float: 1); + } + )", + Lang_CXX03, genericSelectionExpr(), genericSelectionExpr(), false); + + // On Windows, delayed template processing means that both functions become + // null ASTs, so the EXPECT_FALSE does not hold. So, this test only checks for + // differing ASTs if at least one of the cases is not nullptr. + if (std::get<0>(t).S != nullptr || std::get<1>(t).S != nullptr) { + EXPECT_FALSE(testStructuralMatch(t)); + } +} TEST_F(StructuralEquivalenceStmtTest, IntegerLiteral) { auto t = makeWrappedStmts("1", "1", Lang_CXX03, integerLiteral()); EXPECT_TRUE(testStructuralMatch(t)); Index: clang/unittests/AST/DeclMatcher.h =================================================================== --- clang/unittests/AST/DeclMatcher.h +++ clang/unittests/AST/DeclMatcher.h @@ -29,13 +29,21 @@ public: // Returns the first/last matched node under the tree rooted in `D`. template <typename MatcherType> - NodeType *match(const Decl *D, const MatcherType &AMatcher) { + NodeType *match(const Decl *D, const MatcherType &AMatcher, + bool AssertNonNull) { MatchFinder Finder; Finder.addMatcher(AMatcher.bind(""), this); Finder.matchAST(D->getASTContext()); - assert(Node); + if (AssertNonNull) + assert(Node); return Node; } + + // Returns the first/last matched node under the tree rooted in `D`. + template <typename MatcherType> + NodeType *match(const Decl *D, const MatcherType &AMatcher) { + return match(D, AMatcher, true); + } }; template <typename NodeType> using LastDeclMatcher = DeclMatcher<NodeType, DeclMatcherKind::Last>; Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ clang/unittests/AST/ASTImporterTest.cpp @@ -279,6 +279,15 @@ 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( @@ -1088,6 +1097,36 @@ 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; Index: clang/test/ASTMerge/generic-selection-expr/test.cpp =================================================================== --- /dev/null +++ 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 Index: clang/test/ASTMerge/generic-selection-expr/test.c =================================================================== --- /dev/null +++ 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 Index: clang/test/ASTMerge/generic-selection-expr/Inputs/generic.cpp =================================================================== --- /dev/null +++ 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"); +} Index: clang/test/ASTMerge/generic-selection-expr/Inputs/generic.c =================================================================== --- /dev/null +++ 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"); +} Index: clang/lib/Analysis/ExprMutationAnalyzer.cpp =================================================================== --- clang/lib/Analysis/ExprMutationAnalyzer.cpp +++ clang/lib/Analysis/ExprMutationAnalyzer.cpp @@ -95,10 +95,6 @@ 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); Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -241,6 +241,7 @@ REGISTER_MATCHER(functionProtoType); REGISTER_MATCHER(functionTemplateDecl); REGISTER_MATCHER(functionType); + REGISTER_MATCHER(genericSelectionExpr); REGISTER_MATCHER(gnuNullExpr); REGISTER_MATCHER(gotoStmt); REGISTER_MATCHER(has); Index: clang/lib/ASTMatchers/ASTMatchersInternal.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -906,6 +906,8 @@ 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> Index: clang/lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- clang/lib/AST/ASTStructuralEquivalence.cpp +++ clang/lib/AST/ASTStructuralEquivalence.cpp @@ -233,6 +233,24 @@ 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 different 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(), Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -587,6 +587,7 @@ 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); @@ -6536,6 +6537,40 @@ 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(); Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2362,6 +2362,10 @@ 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 Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -1704,6 +1704,11 @@ </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>
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits