Skip to content

Commit

Permalink
[clang][Interp] Handle undefined functions better
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D136936
  • Loading branch information
tbaederr committed Nov 30, 2022
1 parent 8095b09 commit c3380c3
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 17 deletions.
20 changes: 13 additions & 7 deletions clang/lib/AST/Interp/ByteCodeEmitter.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/AST/Interp/ByteCodeExprGen.cpp
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/Context.cpp
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Function.h
Expand Up @@ -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:
Expand All @@ -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; }
Expand Down Expand Up @@ -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().
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/Interp/Interp.cpp
Expand Up @@ -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();

Expand Down
6 changes: 4 additions & 2 deletions clang/lib/AST/Interp/Interp.h
Expand Up @@ -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);
Expand Down Expand Up @@ -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();

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/Interp/Program.cpp
Expand Up @@ -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();
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Program.h
Expand Up @@ -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;
Expand Down
17 changes: 14 additions & 3 deletions 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();
Expand Down Expand Up @@ -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, "");

0 comments on commit c3380c3

Please sign in to comment.