Author: Aaron Ballman Date: 2022-03-21T15:05:53-04:00 New Revision: 403d7d8d7093d6637a006f8b3f75382294259d3f
URL: https://github.com/llvm/llvm-project/commit/403d7d8d7093d6637a006f8b3f75382294259d3f DIFF: https://github.com/llvm/llvm-project/commit/403d7d8d7093d6637a006f8b3f75382294259d3f.diff LOG: Ignore FullExpr when traversing cast sub-expressions Full-expressions are Sema-generated implicit nodes that cover constant-expressions and expressions-with-cleanup for temporaries. Ignore those as part of implicit-ignore, and also remove too-aggressive IgnoreImplicit (which includes nested ImplicitCastExprs, for example) on unpacked sub-expressions. Add some unittests to demonstrate that RecursiveASTVisitor sees through ConstantExpr nodes correctly. Adjust cxx2a-consteval test to cover diagnostics for nested consteval expressions that were previously missed. Fixes bug #53044. Added: Modified: clang/lib/AST/Expr.cpp clang/test/SemaCXX/cxx2a-consteval.cpp clang/unittests/Tooling/CastExprTest.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index e4c441a977fd2..49765b8a25ba6 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1871,6 +1871,9 @@ Expr *ignoreImplicitSemaNodes(Expr *E) { if (auto *Binder = dyn_cast<CXXBindTemporaryExpr>(E)) return Binder->getSubExpr(); + if (auto *Full = dyn_cast<FullExpr>(E)) + return Full->getSubExpr(); + return E; } } // namespace @@ -1884,11 +1887,9 @@ Expr *CastExpr::getSubExprAsWritten() { // Conversions by constructor and conversion functions have a // subexpression describing the call; strip it off. if (E->getCastKind() == CK_ConstructorConversion) { - SubExpr = IgnoreExprNodes( - cast<CXXConstructExpr>(SubExpr->IgnoreImplicit())->getArg(0), - ignoreImplicitSemaNodes); + SubExpr = IgnoreExprNodes(cast<CXXConstructExpr>(SubExpr)->getArg(0), + ignoreImplicitSemaNodes); } else if (E->getCastKind() == CK_UserDefinedConversion) { - SubExpr = SubExpr->IgnoreImplicit(); assert((isa<CXXMemberCallExpr>(SubExpr) || isa<BlockExpr>(SubExpr)) && "Unexpected SubExpr for CK_UserDefinedConversion."); if (auto *MCE = dyn_cast<CXXMemberCallExpr>(SubExpr)) diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp index 4d59261104ed2..275e5df766fa3 100644 --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -359,22 +359,34 @@ void test() { // expected-note@-1 {{is not a constant expression}} { A k = to_lvalue_ref(A()); } // expected-error {{is not a constant expression}} // expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}} - { A k = to_lvalue_ref(A().ret_a()); } // expected-error {{is not a constant expression}} - // expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}} + { A k = to_lvalue_ref(A().ret_a()); } + // expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}} + // expected-note@-2 {{heap-allocated object is not a constant expression}} + // expected-error@-3 {{'alloc::to_lvalue_ref' is not a constant expression}} + // expected-note@-4 {{reference to temporary is not a constant expression}} + // expected-note@-5 {{temporary created here}} { int k = A().ret_a().ret_i(); } + // expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}} + // expected-note@-2 {{heap-allocated object is not a constant expression}} { int k = by_value_a(A()); } { int k = const_a_ref(A()); } { int k = const_a_ref(a); } { int k = rvalue_ref(A()); } { int k = rvalue_ref(std::move(a)); } { int k = const_a_ref(A().ret_a()); } + // expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}} + // expected-note@-2 {{is not a constant expression}} { int k = const_a_ref(to_lvalue_ref(A().ret_a())); } + // expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}} + // expected-note@-2 {{is not a constant expression}} { int k = const_a_ref(to_lvalue_ref(std::move(a))); } { int k = by_value_a(A().ret_a()); } { int k = by_value_a(to_lvalue_ref(static_cast<const A&&>(a))); } { int k = (A().ret_a(), A().ret_i()); }// expected-error {{is not a constant expression}} // expected-note@-1 {{is not a constant expression}} { int k = (const_a_ref(A().ret_a()), A().ret_i()); } + // expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}} + // expected-note@-2 {{is not a constant expression}} } } diff --git a/clang/unittests/Tooling/CastExprTest.cpp b/clang/unittests/Tooling/CastExprTest.cpp index a07eff36afb05..eab23a5a98e5d 100644 --- a/clang/unittests/Tooling/CastExprTest.cpp +++ b/clang/unittests/Tooling/CastExprTest.cpp @@ -14,12 +14,19 @@ namespace { struct CastExprVisitor : TestVisitor<CastExprVisitor> { std::function<void(ExplicitCastExpr *)> OnExplicitCast; + std::function<void(CastExpr *)> OnCast; bool VisitExplicitCastExpr(ExplicitCastExpr *Expr) { if (OnExplicitCast) OnExplicitCast(Expr); return true; } + + bool VisitCastExpr(CastExpr *Expr) { + if (OnCast) + OnCast(Expr); + return true; + } }; TEST(CastExprTest, GetSubExprAsWrittenThroughMaterializedTemporary) { @@ -54,4 +61,57 @@ TEST(CastExprTest, GetSubExprAsWrittenThroughConstantExpr) { CastExprVisitor::Lang_CXX2a); } +// Verify that getConversionFunction looks through a ConstantExpr for implicit +// constructor conversions (https://github.com/llvm/llvm-project/issues/53044): +// +// `-ImplicitCastExpr 'X' <ConstructorConversion> +// `-ConstantExpr 'X' +// |-value: Struct +// `-CXXConstructExpr 'X' 'void (const char *)' +// `-ImplicitCastExpr 'const char *' <ArrayToPointerDecay> +// `-StringLiteral 'const char [7]' lvalue "foobar" +TEST(CastExprTest, GetCtorConversionFunctionThroughConstantExpr) { + CastExprVisitor Visitor; + Visitor.OnCast = [](CastExpr *Expr) { + if (Expr->getCastKind() == CK_ConstructorConversion) { + auto *Conv = Expr->getConversionFunction(); + EXPECT_TRUE(isa<CXXConstructorDecl>(Conv)) + << "Expected CXXConstructorDecl, but saw " << Conv->getDeclKindName(); + } + }; + Visitor.runOver("struct X { consteval X(const char *) {} };\n" + "void f() { X x = \"foobar\"; }\n", + CastExprVisitor::Lang_CXX2a); +} + +// Verify that getConversionFunction looks through a ConstantExpr for implicit +// user-defined conversions. +// +// `-ImplicitCastExpr 'const char *' <UserDefinedConversion> +// `-ConstantExpr 'const char *' +// |-value: LValue +// `-CXXMemberCallExpr 'const char *' +// `-MemberExpr '<bound member function type>' .operator const char * +// `-DeclRefExpr 'const X' lvalue Var 'x' 'const X' +TEST(CastExprTest, GetUserDefinedConversionFunctionThroughConstantExpr) { + CastExprVisitor Visitor; + Visitor.OnCast = [](CastExpr *Expr) { + if (Expr->getCastKind() == CK_UserDefinedConversion) { + auto *Conv = Expr->getConversionFunction(); + EXPECT_TRUE(isa<CXXMethodDecl>(Conv)) + << "Expected CXXMethodDecl, but saw " << Conv->getDeclKindName(); + } + }; + Visitor.runOver("struct X {\n" + " consteval operator const char *() const {\n" + " return nullptr;\n" + " }\n" + "};\n" + "const char *f() {\n" + " constexpr X x;\n" + " return x;\n" + "}\n", + CastExprVisitor::Lang_CXX2a); +} + } // namespace _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits