Skip to content

Commit c3380c3

Browse files
committed
[clang][Interp] Handle undefined functions better
Differential Revision: https://reviews.llvm.org/D136936
1 parent 8095b09 commit c3380c3

File tree

9 files changed

+50
-17
lines changed

9 files changed

+50
-17
lines changed

clang/lib/AST/Interp/ByteCodeEmitter.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ using Error = llvm::Error;
2121

2222
Expected<Function *>
2323
ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
24-
// Do not try to compile undefined functions.
25-
if (!FuncDecl->isDefined(FuncDecl) ||
26-
(!FuncDecl->hasBody() && FuncDecl->willHaveBody()))
27-
return nullptr;
24+
// Function is not defined at all or not yet. We will
25+
// create a Function instance but not compile the body. That
26+
// will (maybe) happen later.
27+
bool HasBody = FuncDecl->hasBody(FuncDecl);
2828

2929
// Set up argument indices.
3030
unsigned ParamOffset = 0;
@@ -65,9 +65,15 @@ ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
6565
}
6666

6767
// Create a handle over the emitted code.
68-
Function *Func =
69-
P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),
70-
std::move(ParamDescriptors), HasThisPointer, HasRVO);
68+
Function *Func = P.getFunction(FuncDecl);
69+
if (!Func)
70+
Func =
71+
P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),
72+
std::move(ParamDescriptors), HasThisPointer, HasRVO);
73+
assert(Func);
74+
if (!HasBody)
75+
return Func;
76+
7177
// Compile the function body.
7278
if (!FuncDecl->isConstexpr() || !visitFunc(FuncDecl)) {
7379
// Return a dummy function if compilation failed.

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,8 +1102,13 @@ template <class Emitter>
11021102
const Function *ByteCodeExprGen<Emitter>::getFunction(const FunctionDecl *FD) {
11031103
assert(FD);
11041104
const Function *Func = P.getFunction(FD);
1105+
bool IsBeingCompiled = Func && !Func->isFullyCompiled();
1106+
bool WasNotDefined = Func && !Func->hasBody();
11051107

1106-
if (!Func) {
1108+
if (IsBeingCompiled)
1109+
return Func;
1110+
1111+
if (!Func || WasNotDefined) {
11071112
if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(Ctx, P).compileFunc(FD))
11081113
Func = *R;
11091114
else {

clang/lib/AST/Interp/Context.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Context::~Context() {}
2929
bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
3030
assert(Stk.empty());
3131
Function *Func = P->getFunction(FD);
32-
if (!Func) {
32+
if (!Func || !Func->hasBody()) {
3333
if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD)) {
3434
Func = *R;
3535
} else {

clang/lib/AST/Interp/Function.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ class Function final {
135135

136136
bool hasThisPointer() const { return HasThisPointer; }
137137

138+
// Checks if the funtion already has a body attached.
139+
bool hasBody() const { return HasBody; }
140+
138141
unsigned getNumParams() const { return ParamTypes.size(); }
139142

140143
private:
@@ -152,6 +155,7 @@ class Function final {
152155
SrcMap = std::move(NewSrcMap);
153156
Scopes = std::move(NewScopes);
154157
IsValid = true;
158+
HasBody = true;
155159
}
156160

157161
void setIsFullyCompiled(bool FC) { IsFullyCompiled = FC; }
@@ -192,6 +196,8 @@ class Function final {
192196
/// the return value is constructed in the caller's stack frame.
193197
/// This is done for functions that return non-primive values.
194198
bool HasRVO = false;
199+
/// If we've already compiled the function's body.
200+
bool HasBody = false;
195201

196202
public:
197203
/// Dumps the disassembled bytecode to \c llvm::errs().

clang/lib/AST/Interp/Interp.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,17 +331,18 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
331331
return true;
332332
}
333333

334-
bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) {
335-
const SourceLocation &Loc = S.Current->getLocation(OpPC);
334+
bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
336335

337336
if (F->isVirtual()) {
338337
if (!S.getLangOpts().CPlusPlus20) {
338+
const SourceLocation &Loc = S.Current->getLocation(OpPC);
339339
S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
340340
return false;
341341
}
342342
}
343343

344344
if (!F->isConstexpr()) {
345+
const SourceLocation &Loc = S.Current->getLocation(OpPC);
345346
if (S.getLangOpts().CPlusPlus11) {
346347
const FunctionDecl *DiagDecl = F->getDecl();
347348

clang/lib/AST/Interp/Interp.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
8383
bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
8484

8585
/// Checks if a method can be called.
86-
bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F);
86+
bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F);
8787

8888
/// Checks the 'this' pointer.
8989
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
@@ -1243,9 +1243,11 @@ inline bool Call(InterpState &S, CodePtr &PC, const Function *Func) {
12431243
if (!CheckInvoke(S, PC, NewFrame->getThis())) {
12441244
return false;
12451245
}
1246-
// TODO: CheckCallable
12471246
}
12481247

1248+
if (!CheckCallable(S, PC, Func))
1249+
return false;
1250+
12491251
InterpFrame *FrameBefore = S.Current;
12501252
S.Current = NewFrame.get();
12511253

clang/lib/AST/Interp/Program.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
204204
}
205205

206206
Function *Program::getFunction(const FunctionDecl *F) {
207-
F = F->getDefinition();
207+
F = F->getCanonicalDecl();
208+
assert(F);
208209
auto It = Funcs.find(F);
209210
return It == Funcs.end() ? nullptr : It->second.get();
210211
}

clang/lib/AST/Interp/Program.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class Program final {
9393
/// Creates a new function from a code range.
9494
template <typename... Ts>
9595
Function *createFunction(const FunctionDecl *Def, Ts &&... Args) {
96+
Def = Def->getCanonicalDecl();
9697
auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...);
9798
Funcs.insert({Def, std::unique_ptr<Function>(Func)});
9899
return Func;

clang/test/AST/Interp/functions.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
22
// RUN: %clang_cc1 -verify=ref %s
33

4-
// expected-no-diagnostics
5-
// ref-no-diagnostics
6-
74
constexpr void doNothing() {}
85
constexpr int gimme5() {
96
doNothing();
@@ -73,3 +70,17 @@ constexpr decltype(N) getNum() {
7370
static_assert(getNum<-2>() == -2, "");
7471
static_assert(getNum<10>() == 10, "");
7572
static_assert(getNum() == 5, "");
73+
74+
constexpr int f(); // expected-note {{declared here}} \
75+
// ref-note {{declared here}}
76+
static_assert(f() == 5, ""); // expected-error {{not an integral constant expression}} \
77+
// expected-note {{undefined function 'f'}} \
78+
// ref-error {{not an integral constant expression}} \
79+
// ref-note {{undefined function 'f'}}
80+
constexpr int a() {
81+
return f();
82+
}
83+
constexpr int f() {
84+
return 5;
85+
}
86+
static_assert(a() == 5, "");

0 commit comments

Comments
 (0)