diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -21,10 +21,10 @@ Expected 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 @@ } // 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 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1102,8 +1102,13 @@ const Function *ByteCodeExprGen::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(Ctx, P).compileFunc(FD)) Func = *R; else { diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -29,7 +29,7 @@ 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(*this, *P).compileFunc(FD)) { Func = *R; } else { diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -135,6 +135,9 @@ 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 @@ SrcMap = std::move(NewSrcMap); Scopes = std::move(NewScopes); IsValid = true; + HasBody = true; } void setIsFullyCompiled(bool FC) { IsFullyCompiled = FC; } @@ -192,6 +196,8 @@ /// 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.h b/clang/lib/AST/Interp/Interp.h --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -83,7 +83,7 @@ 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 @@ 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/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -331,17 +331,18 @@ 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/Program.h b/clang/lib/AST/Interp/Program.h --- a/clang/lib/AST/Interp/Program.h +++ b/clang/lib/AST/Interp/Program.h @@ -93,6 +93,7 @@ /// Creates a new function from a code range. template Function *createFunction(const FunctionDecl *Def, Ts &&... Args) { + Def = Def->getCanonicalDecl(); auto *Func = new Function(*this, Def, std::forward(Args)...); Funcs.insert({Def, std::unique_ptr(Func)}); return Func; diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp --- a/clang/lib/AST/Interp/Program.cpp +++ b/clang/lib/AST/Interp/Program.cpp @@ -204,7 +204,8 @@ } 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/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp --- 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 @@ 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, "");