[PATCH] D157619: [clang][Interp] Reject calling virtual constexpr functions in pre-c++20

2023-09-05 Thread Timm Bäder via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5704dde30735: [clang][Interp] Reject calling virtual 
constexpr functions in pre-c++20 (authored by tbaeder).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D157619/new/

https://reviews.llvm.org/D157619

Files:
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/Context.cpp
  clang/lib/AST/Interp/Context.h
  clang/lib/AST/Interp/Interp.h
  clang/test/AST/Interp/records.cpp

Index: clang/test/AST/Interp/records.cpp
===
--- clang/test/AST/Interp/records.cpp
+++ clang/test/AST/Interp/records.cpp
@@ -871,6 +871,35 @@
 };
 #endif
 
+#if __cplusplus < 202002L
+namespace VirtualFromBase {
+  struct S1 {
+virtual int f() const;
+  };
+  struct S2 {
+virtual int f();
+  };
+  template  struct X : T {
+constexpr X() {}
+double d = 0.0;
+constexpr int f() { return sizeof(T); }
+  };
+
+  // Non-virtual f(), OK.
+  constexpr X> xxs1;
+  constexpr X *p = const_cast>*>();
+  static_assert(p->f() == sizeof(S1), "");
+
+  // Virtual f(), not OK.
+  constexpr X> xxs2;
+  constexpr X *q = const_cast>*>();
+  static_assert(q->f() == sizeof(X), ""); // ref-error {{not an integral constant expression}} \
+  // ref-note {{cannot evaluate call to virtual function}} \
+  // expected-error {{not an integral constant expression}} \
+  // expected-note {{cannot evaluate call to virtual function}}
+}
+#endif
+
 namespace CompositeDefaultArgs {
   struct Foo {
 int a;
Index: clang/lib/AST/Interp/Interp.h
===
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -1765,7 +1765,15 @@
   DynamicDecl, StaticDecl, InitialFunction);
 
   if (Overrider != InitialFunction) {
-Func = S.P.getFunction(Overrider);
+// DR1872: An instantiated virtual constexpr function can't be called in a
+// constant expression (prior to C++20). We can still constant-fold such a
+// call.
+if (!S.getLangOpts().CPlusPlus20 && Overrider->isVirtual()) {
+  const Expr *E = S.Current->getExpr(OpPC);
+  S.CCEDiag(E, diag::note_constexpr_virtual_call) << E->getSourceRange();
+}
+
+Func = S.getContext().getOrCreateFunction(Overrider);
 
 const CXXRecordDecl *ThisFieldDecl =
 ThisPtr.getFieldDesc()->getType()->getAsCXXRecordDecl();
Index: clang/lib/AST/Interp/Context.h
===
--- clang/lib/AST/Interp/Context.h
+++ clang/lib/AST/Interp/Context.h
@@ -72,6 +72,9 @@
   getOverridingFunction(const CXXRecordDecl *DynamicDecl,
 const CXXRecordDecl *StaticDecl,
 const CXXMethodDecl *InitialFunction) const;
+
+  const Function *getOrCreateFunction(const FunctionDecl *FD);
+
   /// Returns whether we should create a global variable for the
   /// given ValueDecl.
   static bool shouldBeGloballyIndexed(const ValueDecl *VD) {
Index: clang/lib/AST/Interp/Context.cpp
===
--- clang/lib/AST/Interp/Context.cpp
+++ clang/lib/AST/Interp/Context.cpp
@@ -210,3 +210,24 @@
   "Couldn't find an overriding function in the class hierarchy?");
   return nullptr;
 }
+
+const Function *Context::getOrCreateFunction(const FunctionDecl *FD) {
+  assert(FD);
+  const Function *Func = P->getFunction(FD);
+  bool IsBeingCompiled = Func && !Func->isFullyCompiled();
+  bool WasNotDefined = Func && !Func->isConstexpr() && !Func->hasBody();
+
+  if (IsBeingCompiled)
+return Func;
+
+  if (!Func || WasNotDefined) {
+if (auto R = ByteCodeStmtGen(*this, *P).compileFunc(FD))
+  Func = *R;
+else {
+  llvm::consumeError(R.takeError());
+  return nullptr;
+}
+  }
+
+  return Func;
+}
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1816,24 +1816,7 @@
 
 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->isConstexpr() && !Func->hasBody();
-
-  if (IsBeingCompiled)
-return Func;
-
-  if (!Func || WasNotDefined) {
-if (auto R = ByteCodeStmtGen(Ctx, P).compileFunc(FD))
-  Func = *R;
-else {
-  llvm::consumeError(R.takeError());
-  return nullptr;
-}
-  }
-
-  return Func;
+  return Ctx.getOrCreateFunction(FD);
 }
 
 template 
___
cfe-commits mailing 

[PATCH] D157619: [clang][Interp] Reject calling virtual constexpr functions in pre-c++20

2023-08-10 Thread Aaron Ballman via Phabricator via cfe-commits
aaron.ballman accepted this revision.
aaron.ballman added a comment.
This revision is now accepted and ready to land.

LGTM!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D157619/new/

https://reviews.llvm.org/D157619

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D157619: [clang][Interp] Reject calling virtual constexpr functions in pre-c++20

2023-08-10 Thread Timm Bäder via Phabricator via cfe-commits
tbaeder created this revision.
tbaeder added reviewers: aaron.ballman, erichkeane, shafik, cor3ntin.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Create a new `getOrCreateFunction()` in `Context`, so we can lazily compile 
functions when invoking them in `CallVirt`. This was necessary in the added 
test case for example.

Also, reject calling virtual constexpr functions in pre-c++20.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D157619

Files:
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/Context.cpp
  clang/lib/AST/Interp/Context.h
  clang/lib/AST/Interp/Interp.h
  clang/test/AST/Interp/records.cpp

Index: clang/test/AST/Interp/records.cpp
===
--- clang/test/AST/Interp/records.cpp
+++ clang/test/AST/Interp/records.cpp
@@ -857,6 +857,35 @@
 };
 #endif
 
+#if __cplusplus < 202002L
+namespace VirtualFromBase {
+  struct S1 {
+virtual int f() const;
+  };
+  struct S2 {
+virtual int f();
+  };
+  template  struct X : T {
+constexpr X() {}
+double d = 0.0;
+constexpr int f() { return sizeof(T); }
+  };
+
+  // Non-virtual f(), OK.
+  constexpr X> xxs1;
+  constexpr X *p = const_cast>*>();
+  static_assert(p->f() == sizeof(S1), "");
+
+  // Virtual f(), not OK.
+  constexpr X> xxs2;
+  constexpr X *q = const_cast>*>();
+  static_assert(q->f() == sizeof(X), ""); // ref-error {{not an integral constant expression}} \
+  // ref-note {{cannot evaluate call to virtual function}} \
+  // expected-error {{not an integral constant expression}} \
+  // expected-note {{cannot evaluate call to virtual function}}
+}
+#endif
+
 namespace CompositeDefaultArgs {
   struct Foo {
 int a;
Index: clang/lib/AST/Interp/Interp.h
===
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -1835,7 +1835,15 @@
   DynamicDecl, StaticDecl, InitialFunction);
 
   if (Overrider != InitialFunction) {
-Func = S.P.getFunction(Overrider);
+// DR1872: An instantiated virtual constexpr function can't be called in a
+// constant expression (prior to C++20). We can still constant-fold such a
+// call.
+if (!S.getLangOpts().CPlusPlus20 && Overrider->isVirtual()) {
+  const Expr *E = S.Current->getExpr(OpPC);
+  S.CCEDiag(E, diag::note_constexpr_virtual_call) << E->getSourceRange();
+}
+
+Func = S.getContext().getOrCreateFunction(Overrider);
 
 const CXXRecordDecl *ThisFieldDecl =
 ThisPtr.getFieldDesc()->getType()->getAsCXXRecordDecl();
Index: clang/lib/AST/Interp/Context.h
===
--- clang/lib/AST/Interp/Context.h
+++ clang/lib/AST/Interp/Context.h
@@ -72,6 +72,9 @@
   getOverridingFunction(const CXXRecordDecl *DynamicDecl,
 const CXXRecordDecl *StaticDecl,
 const CXXMethodDecl *InitialFunction) const;
+
+  const Function *getOrCreateFunction(const FunctionDecl *FD);
+
   /// Returns whether we should create a global variable for the
   /// given ValueDecl.
   static bool shouldBeGloballyIndexed(const ValueDecl *VD) {
Index: clang/lib/AST/Interp/Context.cpp
===
--- clang/lib/AST/Interp/Context.cpp
+++ clang/lib/AST/Interp/Context.cpp
@@ -215,3 +215,24 @@
   "Couldn't find an overriding function in the class hierarchy?");
   return nullptr;
 }
+
+const Function *Context::getOrCreateFunction(const FunctionDecl *FD) {
+  assert(FD);
+  const Function *Func = P->getFunction(FD);
+  bool IsBeingCompiled = Func && !Func->isFullyCompiled();
+  bool WasNotDefined = Func && !Func->isConstexpr() && !Func->hasBody();
+
+  if (IsBeingCompiled)
+return Func;
+
+  if (!Func || WasNotDefined) {
+if (auto R = ByteCodeStmtGen(*this, *P).compileFunc(FD))
+  Func = *R;
+else {
+  llvm::consumeError(R.takeError());
+  return nullptr;
+}
+  }
+
+  return Func;
+}
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -2063,24 +2063,7 @@
 
 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->isConstexpr() && !Func->hasBody();
-
-  if (IsBeingCompiled)
-return Func;
-
-  if (!Func || WasNotDefined) {
-if (auto R = ByteCodeStmtGen(Ctx, P).compileFunc(FD))
-  Func = *R;
-else {
-  llvm::consumeError(R.takeError());
-  return