[clang] c3380c3 - [clang][Interp] Handle undefined functions better
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 30 01:22:06 PST 2022
Author: Timm Bäder
Date: 2022-11-30T10:09:52+01:00
New Revision: c3380c32f856925733a113c12cdeb3c3cb369f1f
URL: https://github.com/llvm/llvm-project/commit/c3380c32f856925733a113c12cdeb3c3cb369f1f
DIFF: https://github.com/llvm/llvm-project/commit/c3380c32f856925733a113c12cdeb3c3cb369f1f.diff
LOG: [clang][Interp] Handle undefined functions better
Differential Revision: https://reviews.llvm.org/D136936
Added:
Modified:
clang/lib/AST/Interp/ByteCodeEmitter.cpp
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/Context.cpp
clang/lib/AST/Interp/Function.h
clang/lib/AST/Interp/Interp.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/Program.cpp
clang/lib/AST/Interp/Program.h
clang/test/AST/Interp/functions.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
index 5bd6d51e78ab..9fd830bcc046 100644
--- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
@@ -21,10 +21,10 @@ using Error = llvm::Error;
Expected<Function *>
ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
- // Do not try to compile undefined functions.
- if (!FuncDecl->isDefined(FuncDecl) ||
- (!FuncDecl->hasBody() && FuncDecl->willHaveBody()))
- return nullptr;
+ // Function is not defined at all or not yet. We will
+ // create a Function instance but not compile the body. That
+ // will (maybe) happen later.
+ bool HasBody = FuncDecl->hasBody(FuncDecl);
// Set up argument indices.
unsigned ParamOffset = 0;
@@ -65,9 +65,15 @@ ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
}
// Create a handle over the emitted code.
- Function *Func =
- P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),
- std::move(ParamDescriptors), HasThisPointer, HasRVO);
+ Function *Func = P.getFunction(FuncDecl);
+ if (!Func)
+ Func =
+ P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),
+ std::move(ParamDescriptors), HasThisPointer, HasRVO);
+ assert(Func);
+ if (!HasBody)
+ return Func;
+
// Compile the function body.
if (!FuncDecl->isConstexpr() || !visitFunc(FuncDecl)) {
// Return a dummy function if compilation failed.
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 49ba9d150503..48d4e98bf2eb 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1102,8 +1102,13 @@ template <class Emitter>
const Function *ByteCodeExprGen<Emitter>::getFunction(const FunctionDecl *FD) {
assert(FD);
const Function *Func = P.getFunction(FD);
+ bool IsBeingCompiled = Func && !Func->isFullyCompiled();
+ bool WasNotDefined = Func && !Func->hasBody();
- if (!Func) {
+ if (IsBeingCompiled)
+ return Func;
+
+ if (!Func || WasNotDefined) {
if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(Ctx, P).compileFunc(FD))
Func = *R;
else {
diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp
index 495d019714ba..1ca3d7515c57 100644
--- a/clang/lib/AST/Interp/Context.cpp
+++ b/clang/lib/AST/Interp/Context.cpp
@@ -29,7 +29,7 @@ Context::~Context() {}
bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
assert(Stk.empty());
Function *Func = P->getFunction(FD);
- if (!Func) {
+ if (!Func || !Func->hasBody()) {
if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD)) {
Func = *R;
} else {
diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h
index f73a6d3f3c9c..5b2a77f1a12d 100644
--- a/clang/lib/AST/Interp/Function.h
+++ b/clang/lib/AST/Interp/Function.h
@@ -135,6 +135,9 @@ class Function final {
bool hasThisPointer() const { return HasThisPointer; }
+ // Checks if the funtion already has a body attached.
+ bool hasBody() const { return HasBody; }
+
unsigned getNumParams() const { return ParamTypes.size(); }
private:
@@ -152,6 +155,7 @@ class Function final {
SrcMap = std::move(NewSrcMap);
Scopes = std::move(NewScopes);
IsValid = true;
+ HasBody = true;
}
void setIsFullyCompiled(bool FC) { IsFullyCompiled = FC; }
@@ -192,6 +196,8 @@ class Function final {
/// the return value is constructed in the caller's stack frame.
/// This is done for functions that return non-primive values.
bool HasRVO = false;
+ /// If we've already compiled the function's body.
+ bool HasBody = false;
public:
/// Dumps the disassembled bytecode to \c llvm::errs().
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index b22756a80345..224b05ffad19 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -331,17 +331,18 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return true;
}
-bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) {
- const SourceLocation &Loc = S.Current->getLocation(OpPC);
+bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
if (F->isVirtual()) {
if (!S.getLangOpts().CPlusPlus20) {
+ const SourceLocation &Loc = S.Current->getLocation(OpPC);
S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
return false;
}
}
if (!F->isConstexpr()) {
+ const SourceLocation &Loc = S.Current->getLocation(OpPC);
if (S.getLangOpts().CPlusPlus11) {
const FunctionDecl *DiagDecl = F->getDecl();
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 002571e73137..5a6c3f16f8a7 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -83,7 +83,7 @@ bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
/// Checks if a method can be called.
-bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F);
+bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F);
/// Checks the 'this' pointer.
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
@@ -1243,9 +1243,11 @@ inline bool Call(InterpState &S, CodePtr &PC, const Function *Func) {
if (!CheckInvoke(S, PC, NewFrame->getThis())) {
return false;
}
- // TODO: CheckCallable
}
+ if (!CheckCallable(S, PC, Func))
+ return false;
+
InterpFrame *FrameBefore = S.Current;
S.Current = NewFrame.get();
diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp
index 3014062f37f4..85f363533e5f 100644
--- a/clang/lib/AST/Interp/Program.cpp
+++ b/clang/lib/AST/Interp/Program.cpp
@@ -204,7 +204,8 @@ llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
}
Function *Program::getFunction(const FunctionDecl *F) {
- F = F->getDefinition();
+ F = F->getCanonicalDecl();
+ assert(F);
auto It = Funcs.find(F);
return It == Funcs.end() ? nullptr : It->second.get();
}
diff --git a/clang/lib/AST/Interp/Program.h b/clang/lib/AST/Interp/Program.h
index 8ff7e49f7044..94bfd2697d26 100644
--- a/clang/lib/AST/Interp/Program.h
+++ b/clang/lib/AST/Interp/Program.h
@@ -93,6 +93,7 @@ class Program final {
/// Creates a new function from a code range.
template <typename... Ts>
Function *createFunction(const FunctionDecl *Def, Ts &&... Args) {
+ Def = Def->getCanonicalDecl();
auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...);
Funcs.insert({Def, std::unique_ptr<Function>(Func)});
return Func;
diff --git a/clang/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp
index 494133c10cb1..e372f23987e2 100644
--- a/clang/test/AST/Interp/functions.cpp
+++ b/clang/test/AST/Interp/functions.cpp
@@ -1,9 +1,6 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
// RUN: %clang_cc1 -verify=ref %s
-// expected-no-diagnostics
-// ref-no-diagnostics
-
constexpr void doNothing() {}
constexpr int gimme5() {
doNothing();
@@ -73,3 +70,17 @@ constexpr decltype(N) getNum() {
static_assert(getNum<-2>() == -2, "");
static_assert(getNum<10>() == 10, "");
static_assert(getNum() == 5, "");
+
+constexpr int f(); // expected-note {{declared here}} \
+ // ref-note {{declared here}}
+static_assert(f() == 5, ""); // expected-error {{not an integral constant expression}} \
+ // expected-note {{undefined function 'f'}} \
+ // ref-error {{not an integral constant expression}} \
+ // ref-note {{undefined function 'f'}}
+constexpr int a() {
+ return f();
+}
+constexpr int f() {
+ return 5;
+}
+static_assert(a() == 5, "");
More information about the cfe-commits
mailing list