diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1908,6 +1908,9 @@ if (auto *Binder = dyn_cast(E)) return Binder->getSubExpr(); + if (auto *Full = dyn_cast(E)) + return Full->getSubExpr(); + return E; } } // namespace @@ -1921,11 +1924,9 @@ // Conversions by constructor and conversion functions have a // subexpression describing the call; strip it off. if (E->getCastKind() == CK_ConstructorConversion) { - SubExpr = IgnoreExprNodes( - cast(SubExpr->IgnoreImplicit())->getArg(0), - ignoreImplicitSemaNodes); + SubExpr = IgnoreExprNodes(cast(SubExpr)->getArg(0), + ignoreImplicitSemaNodes); } else if (E->getCastKind() == CK_UserDefinedConversion) { - SubExpr = SubExpr->IgnoreImplicit(); assert((isa(SubExpr) || isa(SubExpr)) && "Unexpected SubExpr for CK_UserDefinedConversion."); if (auto *MCE = dyn_cast(SubExpr)) diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -359,22 +359,29 @@ // 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}} - { int k = A().ret_a().ret_i(); } - { int k = by_value_a(A()); } + { A k = to_lvalue_ref(A().ret_a()); } // expected-error {{'alloc::A::ret_a' is not a constant expression}} expected-error {{'alloc::to_lvalue_ref' is not a constant expression}} + // expected-note@-1 {{temporary created here}} + // expected-note@-2 {{heap-allocated object is not a constant expression}} expected-note@-2 {{reference to temporary is not a constant expression}} + { int k = A().ret_a().ret_i(); } // expected-error {{'alloc::A::ret_a' is not a constant expression}} + // expected-note@-1 {{heap-allocated object is not a constant expression}} { 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(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 --- a/clang/unittests/Tooling/CastExprTest.cpp +++ b/clang/unittests/Tooling/CastExprTest.cpp @@ -14,12 +14,19 @@ struct CastExprVisitor : TestVisitor { std::function OnExplicitCast; + std::function 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 'X' +// `-ConstantExpr 'X' +// |-value: Struct +// `-CXXConstructExpr 'X' 'void (const char *)' +// `-ImplicitCastExpr 'const char *' +// `-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(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 *' +// `-ConstantExpr 'const char *' +// |-value: LValue +// `-CXXMemberCallExpr 'const char *' +// `-MemberExpr '' .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(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