Index: clang/lib/AST/Interp/ByteCodeEmitter.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -22,14 +22,12 @@ Expected ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) { bool HasBody = true; - // Do not try to compile undefined functions. - if (!FuncDecl->isDefined(FuncDecl)) - return nullptr; - - // Function is defined but doesn't have a body yet. We will - // create a Function instance but not compile the body. That will - // happen later. - if (!FuncDecl->hasBody() && FuncDecl->willHaveBody()) + + // 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. + if (!FuncDecl->isDefined(FuncDecl) || + (FuncDecl->hasBody() && FuncDecl->willHaveBody())) HasBody = false; // Set up argument indices. @@ -71,11 +69,12 @@ } // Create a handle over the emitted code. - Function *Func = P.getFunction(FuncDecl->getDefinition()); + Function *Func = P.getFunction(FuncDecl); if (!Func) - Func = P.createFunction(FuncDecl->getDefinition(), ParamOffset, std::move(ParamTypes), - std::move(ParamDescriptors), HasThisPointer, HasRVO); - + Func = + P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes), + std::move(ParamDescriptors), HasThisPointer, HasRVO); + assert(Func); if (!HasBody) return Func; Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1172,8 +1172,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 || (Func && !Func->hasBody())) { + if (IsBeingCompiled) + return Func; + + if (!Func || WasNotDefined) { if (auto R = ByteCodeStmtGen(Ctx, P).compileFunc(FD)) Func = *R; else { Index: clang/lib/AST/Interp/Context.cpp =================================================================== --- clang/lib/AST/Interp/Context.cpp +++ 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 { Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ 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); @@ -1347,9 +1347,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(); Index: clang/lib/AST/Interp/Interp.cpp =================================================================== --- clang/lib/AST/Interp/Interp.cpp +++ clang/lib/AST/Interp/Interp.cpp @@ -338,17 +338,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(); Index: clang/lib/AST/Interp/Program.h =================================================================== --- clang/lib/AST/Interp/Program.h +++ clang/lib/AST/Interp/Program.h @@ -94,6 +94,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; Index: clang/lib/AST/Interp/Program.cpp =================================================================== --- clang/lib/AST/Interp/Program.cpp +++ clang/lib/AST/Interp/Program.cpp @@ -207,7 +207,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(); } Index: clang/test/AST/Interp/functions.cpp =================================================================== --- clang/test/AST/Interp/functions.cpp +++ 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(); @@ -74,11 +71,16 @@ static_assert(getNum<10>() == 10, ""); static_assert(getNum() == 5, ""); -constexpr int f(); +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); +static_assert(a() == 5, "");