kimgr created this revision. kimgr requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Under -std=c++20 with use of consteval, the subexpression hierarchy sometimes contains ConstantExpr nodes that getConversionFunction did not expect. CastExpr::getConversionFunction is very rarely used, so this was difficult to trigger in the compiler. It is possible to reproduce using -ast-dump=json, which triggers an assertion for inputs with consteval implicit casts. In AST-based tools, however, it surfaces quite easily if they try to inspect the conversion function of a visited CastExpr. Add two new testcases, both for implicit constructor conversions and user-defined conversions with consteval. Depends on D117390 <https://reviews.llvm.org/D117390> Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D117391 Files: clang/lib/AST/Expr.cpp clang/unittests/Tooling/CastExprTest.cpp Index: clang/unittests/Tooling/CastExprTest.cpp =================================================================== --- clang/unittests/Tooling/CastExprTest.cpp +++ clang/unittests/Tooling/CastExprTest.cpp @@ -14,12 +14,19 @@ 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 @@ CastExprVisitor::Lang_CXX2a); } +// Verify that getConversionFunction looks through a ConstantExpr for implicit +// constructor conversions (https://github.com/llvm/llvm-project/issues/53044): +// +// `-ImplicitCastExpr 'S' <ConstructorConversion> +// `-ConstantExpr 'S' +// |-value: Struct +// `-CXXConstructExpr 'S' '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 Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -1944,6 +1944,7 @@ for (const CastExpr *E = this; E; E = dyn_cast<ImplicitCastExpr>(SubExpr)) { SubExpr = skipImplicitTemporary(E->getSubExpr()); + SubExpr = SubExpr->IgnoreImplicit(); if (E->getCastKind() == CK_ConstructorConversion) return cast<CXXConstructExpr>(SubExpr)->getConstructor();
Index: clang/unittests/Tooling/CastExprTest.cpp =================================================================== --- clang/unittests/Tooling/CastExprTest.cpp +++ clang/unittests/Tooling/CastExprTest.cpp @@ -14,12 +14,19 @@ 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 @@ CastExprVisitor::Lang_CXX2a); } +// Verify that getConversionFunction looks through a ConstantExpr for implicit +// constructor conversions (https://github.com/llvm/llvm-project/issues/53044): +// +// `-ImplicitCastExpr 'S' <ConstructorConversion> +// `-ConstantExpr 'S' +// |-value: Struct +// `-CXXConstructExpr 'S' '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 Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -1944,6 +1944,7 @@ for (const CastExpr *E = this; E; E = dyn_cast<ImplicitCastExpr>(SubExpr)) { SubExpr = skipImplicitTemporary(E->getSubExpr()); + SubExpr = SubExpr->IgnoreImplicit(); if (E->getCastKind() == CK_ConstructorConversion) return cast<CXXConstructExpr>(SubExpr)->getConstructor();
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits