Skip to content

Commit 403d7d8

Browse files
committed
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 llvm#53044.
1 parent 276d214 commit 403d7d8

File tree

3 files changed

+79
-6
lines changed

3 files changed

+79
-6
lines changed

clang/lib/AST/Expr.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,9 @@ Expr *ignoreImplicitSemaNodes(Expr *E) {
18711871
if (auto *Binder = dyn_cast<CXXBindTemporaryExpr>(E))
18721872
return Binder->getSubExpr();
18731873

1874+
if (auto *Full = dyn_cast<FullExpr>(E))
1875+
return Full->getSubExpr();
1876+
18741877
return E;
18751878
}
18761879
} // namespace
@@ -1884,11 +1887,9 @@ Expr *CastExpr::getSubExprAsWritten() {
18841887
// Conversions by constructor and conversion functions have a
18851888
// subexpression describing the call; strip it off.
18861889
if (E->getCastKind() == CK_ConstructorConversion) {
1887-
SubExpr = IgnoreExprNodes(
1888-
cast<CXXConstructExpr>(SubExpr->IgnoreImplicit())->getArg(0),
1889-
ignoreImplicitSemaNodes);
1890+
SubExpr = IgnoreExprNodes(cast<CXXConstructExpr>(SubExpr)->getArg(0),
1891+
ignoreImplicitSemaNodes);
18901892
} else if (E->getCastKind() == CK_UserDefinedConversion) {
1891-
SubExpr = SubExpr->IgnoreImplicit();
18921893
assert((isa<CXXMemberCallExpr>(SubExpr) || isa<BlockExpr>(SubExpr)) &&
18931894
"Unexpected SubExpr for CK_UserDefinedConversion.");
18941895
if (auto *MCE = dyn_cast<CXXMemberCallExpr>(SubExpr))

clang/test/SemaCXX/cxx2a-consteval.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,22 +359,34 @@ void test() {
359359
// expected-note@-1 {{is not a constant expression}}
360360
{ A k = to_lvalue_ref(A()); } // expected-error {{is not a constant expression}}
361361
// expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}}
362-
{ A k = to_lvalue_ref(A().ret_a()); } // expected-error {{is not a constant expression}}
363-
// expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}}
362+
{ A k = to_lvalue_ref(A().ret_a()); }
363+
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
364+
// expected-note@-2 {{heap-allocated object is not a constant expression}}
365+
// expected-error@-3 {{'alloc::to_lvalue_ref' is not a constant expression}}
366+
// expected-note@-4 {{reference to temporary is not a constant expression}}
367+
// expected-note@-5 {{temporary created here}}
364368
{ int k = A().ret_a().ret_i(); }
369+
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
370+
// expected-note@-2 {{heap-allocated object is not a constant expression}}
365371
{ int k = by_value_a(A()); }
366372
{ int k = const_a_ref(A()); }
367373
{ int k = const_a_ref(a); }
368374
{ int k = rvalue_ref(A()); }
369375
{ int k = rvalue_ref(std::move(a)); }
370376
{ int k = const_a_ref(A().ret_a()); }
377+
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
378+
// expected-note@-2 {{is not a constant expression}}
371379
{ int k = const_a_ref(to_lvalue_ref(A().ret_a())); }
380+
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
381+
// expected-note@-2 {{is not a constant expression}}
372382
{ int k = const_a_ref(to_lvalue_ref(std::move(a))); }
373383
{ int k = by_value_a(A().ret_a()); }
374384
{ int k = by_value_a(to_lvalue_ref(static_cast<const A&&>(a))); }
375385
{ int k = (A().ret_a(), A().ret_i()); }// expected-error {{is not a constant expression}}
376386
// expected-note@-1 {{is not a constant expression}}
377387
{ int k = (const_a_ref(A().ret_a()), A().ret_i()); }
388+
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
389+
// expected-note@-2 {{is not a constant expression}}
378390
}
379391

380392
}

clang/unittests/Tooling/CastExprTest.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,19 @@ namespace {
1414

1515
struct CastExprVisitor : TestVisitor<CastExprVisitor> {
1616
std::function<void(ExplicitCastExpr *)> OnExplicitCast;
17+
std::function<void(CastExpr *)> OnCast;
1718

1819
bool VisitExplicitCastExpr(ExplicitCastExpr *Expr) {
1920
if (OnExplicitCast)
2021
OnExplicitCast(Expr);
2122
return true;
2223
}
24+
25+
bool VisitCastExpr(CastExpr *Expr) {
26+
if (OnCast)
27+
OnCast(Expr);
28+
return true;
29+
}
2330
};
2431

2532
TEST(CastExprTest, GetSubExprAsWrittenThroughMaterializedTemporary) {
@@ -54,4 +61,57 @@ TEST(CastExprTest, GetSubExprAsWrittenThroughConstantExpr) {
5461
CastExprVisitor::Lang_CXX2a);
5562
}
5663

64+
// Verify that getConversionFunction looks through a ConstantExpr for implicit
65+
// constructor conversions (https://github.com/llvm/llvm-project/issues/53044):
66+
//
67+
// `-ImplicitCastExpr 'X' <ConstructorConversion>
68+
// `-ConstantExpr 'X'
69+
// |-value: Struct
70+
// `-CXXConstructExpr 'X' 'void (const char *)'
71+
// `-ImplicitCastExpr 'const char *' <ArrayToPointerDecay>
72+
// `-StringLiteral 'const char [7]' lvalue "foobar"
73+
TEST(CastExprTest, GetCtorConversionFunctionThroughConstantExpr) {
74+
CastExprVisitor Visitor;
75+
Visitor.OnCast = [](CastExpr *Expr) {
76+
if (Expr->getCastKind() == CK_ConstructorConversion) {
77+
auto *Conv = Expr->getConversionFunction();
78+
EXPECT_TRUE(isa<CXXConstructorDecl>(Conv))
79+
<< "Expected CXXConstructorDecl, but saw " << Conv->getDeclKindName();
80+
}
81+
};
82+
Visitor.runOver("struct X { consteval X(const char *) {} };\n"
83+
"void f() { X x = \"foobar\"; }\n",
84+
CastExprVisitor::Lang_CXX2a);
85+
}
86+
87+
// Verify that getConversionFunction looks through a ConstantExpr for implicit
88+
// user-defined conversions.
89+
//
90+
// `-ImplicitCastExpr 'const char *' <UserDefinedConversion>
91+
// `-ConstantExpr 'const char *'
92+
// |-value: LValue
93+
// `-CXXMemberCallExpr 'const char *'
94+
// `-MemberExpr '<bound member function type>' .operator const char *
95+
// `-DeclRefExpr 'const X' lvalue Var 'x' 'const X'
96+
TEST(CastExprTest, GetUserDefinedConversionFunctionThroughConstantExpr) {
97+
CastExprVisitor Visitor;
98+
Visitor.OnCast = [](CastExpr *Expr) {
99+
if (Expr->getCastKind() == CK_UserDefinedConversion) {
100+
auto *Conv = Expr->getConversionFunction();
101+
EXPECT_TRUE(isa<CXXMethodDecl>(Conv))
102+
<< "Expected CXXMethodDecl, but saw " << Conv->getDeclKindName();
103+
}
104+
};
105+
Visitor.runOver("struct X {\n"
106+
" consteval operator const char *() const {\n"
107+
" return nullptr;\n"
108+
" }\n"
109+
"};\n"
110+
"const char *f() {\n"
111+
" constexpr X x;\n"
112+
" return x;\n"
113+
"}\n",
114+
CastExprVisitor::Lang_CXX2a);
115+
}
116+
57117
} // namespace

0 commit comments

Comments
 (0)