if (auto *Binder = dyn_cast<CXXBindTemporaryExpr>(E))
return Binder->getSubExpr();
+ if (auto *Full = dyn_cast<FullExpr>(E))
+ return Full->getSubExpr();
+
return E;
}
} // namespace
// 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))
// 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}}
}
}
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) {
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