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, ""); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits