[clang] [WIP][Clang][Interp] Fix display of syntactically-invalid note for member function calls (PR #102170)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 7 07:15:53 PDT 2024
https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/102170
>From 9665cf5b791b89ca9f1f80e408135f052b808b31 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 7 Aug 2024 22:15:31 +0800
Subject: [PATCH] [Clang][Interp] Fix display of syntactically-invalid note for
member function calls
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/AST/Interp/Compiler.cpp | 19 ++++++------
clang/lib/AST/Interp/Context.cpp | 2 +-
clang/lib/AST/Interp/EvalEmitter.cpp | 4 +--
clang/lib/AST/Interp/Interp.cpp | 2 +-
clang/lib/AST/Interp/Interp.h | 19 ++++++------
clang/lib/AST/Interp/InterpFrame.cpp | 31 +++++++++++++------
clang/lib/AST/Interp/InterpFrame.h | 6 ++--
clang/lib/AST/Interp/Opcodes.td | 6 ++--
clang/test/AST/Interp/constexpr-nqueens.cpp | 2 +-
clang/test/AST/Interp/lambda.cpp | 2 +-
clang/test/AST/Interp/records.cpp | 4 +--
.../test/SemaCXX/constexpr-frame-describe.cpp | 5 +++
12 files changed, 62 insertions(+), 40 deletions(-)
diff --git a/clang/lib/AST/Interp/Compiler.cpp b/clang/lib/AST/Interp/Compiler.cpp
index 02cbe38f5fb1f..d0494a5065f5d 100644
--- a/clang/lib/AST/Interp/Compiler.cpp
+++ b/clang/lib/AST/Interp/Compiler.cpp
@@ -2551,10 +2551,10 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
VarArgSize +=
align(primSize(classify(E->getArg(I)->getType()).value_or(PT_Ptr)));
}
- if (!this->emitCallVar(Func, VarArgSize, E))
+ if (!this->emitCallVar(Func, VarArgSize, E, E))
return false;
} else {
- if (!this->emitCall(Func, 0, E))
+ if (!this->emitCall(Func, /*VarArgSize=*/0, E, E))
return false;
}
@@ -2588,7 +2588,7 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
return false;
}
- if (!this->emitCall(Func, 0, E))
+ if (!this->emitCall(Func, /*VarArgSize=*/0, E, E))
return false;
}
return true;
@@ -2799,7 +2799,7 @@ bool Compiler<Emitter>::VisitCXXInheritedCtorInitExpr(
Offset += align(primSize(PT));
}
- return this->emitCall(F, 0, E);
+ return this->emitCall(F, /*VarArgSize=*/0, E, E);
}
template <class Emitter>
@@ -4087,7 +4087,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
for (unsigned I = NumParams, N = E->getNumArgs(); I != N; ++I)
VarArgSize += align(primSize(classify(E->getArg(I)).value_or(PT_Ptr)));
- if (!this->emitCallVirt(Func, VarArgSize, E))
+ if (!this->emitCallVirt(Func, VarArgSize, E, E))
return false;
} else if (Func->isVariadic()) {
uint32_t VarArgSize = 0;
@@ -4095,10 +4095,10 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
Func->getNumWrittenParams() + isa<CXXOperatorCallExpr>(E);
for (unsigned I = NumParams, N = E->getNumArgs(); I != N; ++I)
VarArgSize += align(primSize(classify(E->getArg(I)).value_or(PT_Ptr)));
- if (!this->emitCallVar(Func, VarArgSize, E))
+ if (!this->emitCallVar(Func, VarArgSize, E, E))
return false;
} else {
- if (!this->emitCall(Func, 0, E))
+ if (!this->emitCall(Func, /*VarArgSize=*/0, E, E))
return false;
}
} else {
@@ -4705,7 +4705,7 @@ bool Compiler<Emitter>::emitLambdaStaticInvokerBody(const CXXMethodDecl *MD) {
return false;
}
- if (!this->emitCall(Func, 0, LambdaCallOp))
+ if (!this->emitCall(Func, /*VarArgSize=*/0, /*CE=*/nullptr, LambdaCallOp))
return false;
this->emitCleanup();
@@ -5567,7 +5567,8 @@ bool Compiler<Emitter>::emitRecordDestruction(const Record *R) {
assert(DtorFunc->getNumParams() == 1);
if (!this->emitDupPtr(SourceInfo{}))
return false;
- if (!this->emitCall(DtorFunc, 0, SourceInfo{}))
+ if (!this->emitCall(DtorFunc, /*VarArgSize=*/0, /*CE=*/nullptr,
+ SourceInfo{}))
return false;
}
diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp
index 92ac28137fdb4..6ea61bdc44553 100644
--- a/clang/lib/AST/Interp/Context.cpp
+++ b/clang/lib/AST/Interp/Context.cpp
@@ -206,7 +206,7 @@ bool Context::Run(State &Parent, const Function *Func, APValue &Result) {
{
InterpState State(Parent, *P, Stk, *this);
State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, CodePtr(),
- Func->getArgSize());
+ Func->getArgSize(), /*CE=*/nullptr);
if (Interpret(State, Result)) {
assert(Stk.empty());
return true;
diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp
index 08536536ac3c2..d7e442f37e23f 100644
--- a/clang/lib/AST/Interp/EvalEmitter.cpp
+++ b/clang/lib/AST/Interp/EvalEmitter.cpp
@@ -20,8 +20,8 @@ EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
InterpStack &Stk)
: Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {
// Create a dummy frame for the interpreter which does not have locals.
- S.Current =
- new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr(), 0);
+ S.Current = new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr,
+ CodePtr(), /*ArgSize=*/0, /*CE=*/nullptr);
}
EvalEmitter::~EvalEmitter() {
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 0f72b860ddad7..fe38cfb54fb34 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -872,7 +872,7 @@ static bool runRecordDestructor(InterpState &S, CodePtr OpPC,
return false;
S.Stk.push<Pointer>(BasePtr);
- if (!Call(S, OpPC, DtorFunc, 0))
+ if (!Call(S, OpPC, DtorFunc, /*VarArgSize=*/0, /*CE=*/nullptr))
return false;
}
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 2eed0d3d1f16b..bfe1c24288924 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -2513,7 +2513,7 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
}
inline bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
- uint32_t VarArgSize) {
+ uint32_t VarArgSize, const Expr *CE) {
if (Func->hasThisPointer()) {
size_t ArgSize = Func->getArgSize() + VarArgSize;
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
@@ -2540,7 +2540,7 @@ inline bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
if (!CheckCallDepth(S, OpPC))
return false;
- auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
+ auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize, CE);
InterpFrame *FrameBefore = S.Current;
S.Current = NewFrame.get();
@@ -2563,7 +2563,7 @@ inline bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
}
inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
- uint32_t VarArgSize) {
+ uint32_t VarArgSize, const Expr *CE) {
if (Func->hasThisPointer()) {
size_t ArgSize = Func->getArgSize() + VarArgSize;
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
@@ -2591,7 +2591,7 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
if (!CheckCallDepth(S, OpPC))
return false;
- auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
+ auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize, CE);
InterpFrame *FrameBefore = S.Current;
S.Current = NewFrame.get();
@@ -2612,7 +2612,7 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
}
inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
- uint32_t VarArgSize) {
+ uint32_t VarArgSize, const Expr *CE) {
assert(Func->hasThisPointer());
assert(Func->isVirtual());
size_t ArgSize = Func->getArgSize() + VarArgSize;
@@ -2659,7 +2659,7 @@ inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
}
}
- if (!Call(S, OpPC, Func, VarArgSize))
+ if (!Call(S, OpPC, Func, VarArgSize, CE))
return false;
// Covariant return types. The return type of Overrider is a pointer
@@ -2686,7 +2686,8 @@ inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
inline bool CallBI(InterpState &S, CodePtr &PC, const Function *Func,
const CallExpr *CE) {
- auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC);
+ auto NewFrame =
+ std::make_unique<InterpFrame>(S, Func, PC, /*VarArgSize=*/0, CE);
InterpFrame *FrameBefore = S.Current;
S.Current = NewFrame.get();
@@ -2737,9 +2738,9 @@ inline bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
VarArgSize -= align(primSize(PT_Ptr));
if (F->isVirtual())
- return CallVirt(S, OpPC, F, VarArgSize);
+ return CallVirt(S, OpPC, F, VarArgSize, CE);
- return Call(S, OpPC, F, VarArgSize);
+ return Call(S, OpPC, F, VarArgSize, CE);
}
inline bool GetFnPtr(InterpState &S, CodePtr OpPC, const Function *Func) {
diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp
index 83784db91f4f3..1d285dfb5f621 100644
--- a/clang/lib/AST/Interp/InterpFrame.cpp
+++ b/clang/lib/AST/Interp/InterpFrame.cpp
@@ -18,15 +18,17 @@
#include "Program.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
using namespace clang;
using namespace clang::interp;
InterpFrame::InterpFrame(InterpState &S, const Function *Func,
- InterpFrame *Caller, CodePtr RetPC, unsigned ArgSize)
+ InterpFrame *Caller, CodePtr RetPC, unsigned ArgSize,
+ const clang::Expr *CE)
: Caller(Caller), S(S), Depth(Caller ? Caller->Depth + 1 : 0), Func(Func),
- RetPC(RetPC), ArgSize(ArgSize), Args(static_cast<char *>(S.Stk.top())),
- FrameOffset(S.Stk.size()) {
+ CallExpr(CE), RetPC(RetPC), ArgSize(ArgSize),
+ Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
if (!Func)
return;
@@ -46,8 +48,9 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func,
}
InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC,
- unsigned VarArgSize)
- : InterpFrame(S, Func, S.Current, RetPC, Func->getArgSize() + VarArgSize) {
+ unsigned VarArgSize, const clang::Expr *CE)
+ : InterpFrame(S, Func, S.Current, RetPC, Func->getArgSize() + VarArgSize,
+ CE) {
// As per our calling convention, the this pointer is
// part of the ArgSize.
// If the function has RVO, the RVO pointer is first.
@@ -170,10 +173,20 @@ void InterpFrame::describe(llvm::raw_ostream &OS) const {
return;
const FunctionDecl *F = getCallee();
- if (const auto *M = dyn_cast<CXXMethodDecl>(F);
- M && M->isInstance() && !isa<CXXConstructorDecl>(F)) {
- print(OS, This, S.getCtx(), S.getCtx().getRecordType(M->getParent()));
- OS << "->";
+ if (const auto *MCE = dyn_cast_if_present<CXXMemberCallExpr>(CallExpr)) {
+ const Expr *Object = MCE->getImplicitObjectArgument();
+ Object->printPretty(OS, /*Helper=*/nullptr, S.getCtx().getPrintingPolicy(),
+ /*Indentation=*/0);
+ if (Object->getType()->isPointerType())
+ OS << "->";
+ else
+ OS << ".";
+ } else if (const auto *OCE =
+ dyn_cast_if_present<CXXOperatorCallExpr>(CallExpr)) {
+ OCE->getArg(0)->printPretty(OS, /*Helper=*/nullptr,
+ S.getCtx().getPrintingPolicy(),
+ /*Indentation=*/0);
+ OS << ".";
}
F->getNameForDiagnostic(OS, S.getCtx().getPrintingPolicy(),
diff --git a/clang/lib/AST/Interp/InterpFrame.h b/clang/lib/AST/Interp/InterpFrame.h
index 91b9b41b5d334..6f68275faa3a9 100644
--- a/clang/lib/AST/Interp/InterpFrame.h
+++ b/clang/lib/AST/Interp/InterpFrame.h
@@ -30,14 +30,14 @@ class InterpFrame final : public Frame {
/// Creates a new frame for a method call.
InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller,
- CodePtr RetPC, unsigned ArgSize);
+ CodePtr RetPC, unsigned ArgSize, const Expr *CE);
/// Creates a new frame with the values that make sense.
/// I.e., the caller is the current frame of S,
/// the This() pointer is the current Pointer on the top of S's stack,
/// and the RVO pointer is before that.
InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC,
- unsigned VarArgSize = 0);
+ unsigned VarArgSize = 0, const Expr *CE = nullptr);
/// Destroys the frame, killing all live pointers to stack slots.
~InterpFrame();
@@ -152,6 +152,8 @@ class InterpFrame final : public Frame {
unsigned Depth;
/// Reference to the function being executed.
const Function *Func;
+ /// The syntactical structure of member function calls
+ const Expr *CallExpr;
/// Current object pointer for methods.
Pointer This;
/// Pointer the non-primitive return value gets constructed in.
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 220dff0c556b1..9aabd9198f129 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -198,11 +198,11 @@ def NoRet : Opcode {}
def Call : Opcode {
- let Args = [ArgFunction, ArgUint32];
+ let Args = [ArgFunction, ArgUint32, ArgExpr];
}
def CallVirt : Opcode {
- let Args = [ArgFunction, ArgUint32];
+ let Args = [ArgFunction, ArgUint32, ArgExpr];
}
def CallBI : Opcode {
@@ -214,7 +214,7 @@ def CallPtr : Opcode {
}
def CallVar : Opcode {
- let Args = [ArgFunction, ArgUint32];
+ let Args = [ArgFunction, ArgUint32, ArgExpr];
}
def OffsetOf : Opcode {
diff --git a/clang/test/AST/Interp/constexpr-nqueens.cpp b/clang/test/AST/Interp/constexpr-nqueens.cpp
index 971f99a032b66..ed038dbc9b077 100644
--- a/clang/test/AST/Interp/constexpr-nqueens.cpp
+++ b/clang/test/AST/Interp/constexpr-nqueens.cpp
@@ -49,7 +49,7 @@ constexpr Board buildBoardScan(int N, int Col, int Row, const Board &B) {
return Row == N ? Board(0, true) :
B.ok(Row, Col) ?
tryBoard(buildBoardRecurse(N, Col + 1, B.addQueen(Row, Col)), // ref-note {{in call to 'B.addQueen(0, 0)}} \
- // expected-note {{in call to '&Board()->addQueen(0, 0)}}
+ // expected-note {{in call to 'B.addQueen(0, 0)}}
N, Col, Row+1, B) :
buildBoardScan(N, Col, Row + 1, B);
}
diff --git a/clang/test/AST/Interp/lambda.cpp b/clang/test/AST/Interp/lambda.cpp
index d68fe995e8fa1..27bbebddf4480 100644
--- a/clang/test/AST/Interp/lambda.cpp
+++ b/clang/test/AST/Interp/lambda.cpp
@@ -46,7 +46,7 @@ constexpr int div(int a, int b) {
return a / b; // both-note {{division by zero}}
};
- return f(); // expected-note {{in call to '&f->operator()()'}} \
+ return f(); // expected-note {{in call to 'f.operator()()'}} \
// ref-note {{in call to 'f.operator()()'}}
}
static_assert(div(8, 2) == 4);
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 479c0487fecae..0735d4765a22a 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -335,7 +335,7 @@ namespace InitializerTemporaries {
constexpr int f() {
S{}; // ref-note {{in call to 'S{}.~S()'}} \
- // expected-note {{in call to '&S{}->~S()'}}
+ // expected-note {{in call to '~S()'}}
return 12;
}
static_assert(f() == 12); // both-error {{not an integral constant expression}} \
@@ -599,7 +599,7 @@ namespace Destructors {
};
constexpr int testS() {
S{}; // ref-note {{in call to 'S{}.~S()'}} \
- // expected-note {{in call to '&S{}->~S()'}}
+ // expected-note {{in call to '~S()'}}
return 1;
}
static_assert(testS() == 1); // both-error {{not an integral constant expression}} \
diff --git a/clang/test/SemaCXX/constexpr-frame-describe.cpp b/clang/test/SemaCXX/constexpr-frame-describe.cpp
index 7b832d9a4b4a1..b305c281f92e5 100644
--- a/clang/test/SemaCXX/constexpr-frame-describe.cpp
+++ b/clang/test/SemaCXX/constexpr-frame-describe.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -DNEW_CONST_INTERP -std=c++20 -fexperimental-new-constant-interpreter -fsyntax-only -verify %s
struct Foo {
@@ -67,9 +68,11 @@ struct Bar {
template <typename U, int num>
constexpr int fail2() const { return 1 / 0; } // expected-warning {{division by zero}} \
// expected-note {{division by zero}}
+#ifndef NEW_CONST_INTERP
template <typename ...Args>
constexpr int fail3(Args... args) const { return 1 / 0; } // expected-warning {{division by zero}} \
// expected-note {{division by zero}}
+#endif
};
constexpr Bar<int> bar;
@@ -77,5 +80,7 @@ static_assert(bar.fail1<int>()); // expected-error {{constant expression}} \
// expected-note {{in call to 'bar.fail1<int>()'}}
static_assert(bar.fail2<int*, 42>()); // expected-error {{constant expression}} \
// expected-note {{in call to 'bar.fail2<int *, 42>()'}}
+#ifndef NEW_CONST_INTERP
static_assert(bar.fail3(3, 4UL, bar, &bar)); // expected-error {{constant expression}} \
// expected-note {{in call to 'bar.fail3<int, unsigned long, Bar<int>, const Bar<int> *>(3, 4, {}, &bar)'}}
+#endif
More information about the cfe-commits
mailing list