[clang] c3380c3 - [clang][Interp] Handle undefined functions better

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 30 01:22:06 PST 2022


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, "");


        


More information about the cfe-commits mailing list