From c3380c32f856925733a113c12cdeb3c3cb369f1f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Timm=20B=C3=A4der?= Date: Fri, 28 Oct 2022 10:31:47 +0200 Subject: [PATCH] [clang][Interp] Handle undefined functions better Differential Revision: https://reviews.llvm.org/D136936 --- clang/lib/AST/Interp/ByteCodeEmitter.cpp | 20 +++++++++++++------- clang/lib/AST/Interp/ByteCodeExprGen.cpp | 7 ++++++- clang/lib/AST/Interp/Context.cpp | 2 +- clang/lib/AST/Interp/Function.h | 6 ++++++ clang/lib/AST/Interp/Interp.cpp | 5 +++-- clang/lib/AST/Interp/Interp.h | 6 ++++-- clang/lib/AST/Interp/Program.cpp | 3 ++- clang/lib/AST/Interp/Program.h | 1 + clang/test/AST/Interp/functions.cpp | 17 ++++++++++++++--- 9 files changed, 50 insertions(+), 17 deletions(-) diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index 5bd6d51..9fd830b 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -21,10 +21,10 @@ using Error = llvm::Error; 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 @@ 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 49ba9d1..48d4e98 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1102,8 +1102,13 @@ template 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 index 495d019..1ca3d75 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(*this, *P).compileFunc(FD)) { Func = *R; } else { diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h index f73a6d3..5b2a77f 100644 --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -135,6 +135,9 @@ public: 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 @@ private: SrcMap = std::move(NewSrcMap); Scopes = std::move(NewScopes); IsValid = true; + HasBody = true; } void setIsFullyCompiled(bool FC) { IsFullyCompiled = FC; } @@ -192,6 +196,8 @@ private: /// 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 b22756a..224b05f 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 002571e..5a6c3f1 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 3014062..85f3635 100644 --- a/clang/lib/AST/Interp/Program.cpp +++ b/clang/lib/AST/Interp/Program.cpp @@ -204,7 +204,8 @@ llvm::Optional 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 8ff7e49..94bfd26 100644 --- a/clang/lib/AST/Interp/Program.h +++ b/clang/lib/AST/Interp/Program.h @@ -93,6 +93,7 @@ public: /// 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/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp index 494133c..e372f23 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, ""); -- 2.7.4