Skip to content

Conversation

@tbaederr
Copy link
Contributor

@tbaederr tbaederr commented Oct 27, 2025

Instead of checking the initial callee, check the callee after the virtual dispatch. This means we need to check whether we're in a ctor to not regress existing tests.

Fixes #165234

Instead of checking the initial callee, check the callee after the
virtual dispatch. This means we need to check whether we're in a ctor to
not regress existing tests.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:bytecode Issues for the clang bytecode constexpr interpreter labels Oct 27, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 27, 2025

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

Instead of checking the initial callee, check the callee after the virtual dispatch. This means we need to check whether we're in a ctor to not regress existing tests.

Fixes #165234


Full diff: https://github.com/llvm/llvm-project/pull/165262.diff

2 Files Affected:

  • (modified) clang/lib/AST/ByteCode/Interp.cpp (+15-17)
  • (modified) clang/test/AST/ByteCode/cxx20.cpp (+18)
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 910868b27f48e..b52e8dcac8c96 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1644,8 +1644,8 @@ static bool GetDynamicDecl(InterpState &S, CodePtr OpPC, Pointer TypePtr,
 
   QualType DynamicType = TypePtr.getType();
   if (TypePtr.isStatic() || TypePtr.isConst()) {
-    const VarDecl *VD = TypePtr.getDeclDesc()->asVarDecl();
-    if (!VD->isConstexpr()) {
+    if (const VarDecl *VD = TypePtr.getDeclDesc()->asVarDecl();
+        VD && !VD->isConstexpr()) {
       const Expr *E = S.Current->getExpr(OpPC);
       APValue V = TypePtr.toAPValue(S.getASTContext());
       QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
@@ -1676,20 +1676,6 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
   Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
   const FunctionDecl *Callee = Func->getDecl();
 
-  if (!Func->isFullyCompiled())
-    compileFunction(S, Func);
-
-  // C++2a [class.abstract]p6:
-  //   the effect of making a virtual call to a pure virtual function [...] is
-  //   undefined
-  if (Callee->isPureVirtual()) {
-    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_pure_virtual_call,
-             1)
-        << Callee;
-    S.Note(Callee->getLocation(), diag::note_declared_at);
-    return false;
-  }
-
   const CXXRecordDecl *DynamicDecl = nullptr;
   if (!GetDynamicDecl(S, OpPC, ThisPtr, DynamicDecl))
     return false;
@@ -1699,7 +1685,8 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
   const auto *InitialFunction = cast<CXXMethodDecl>(Callee);
   const CXXMethodDecl *Overrider;
 
-  if (StaticDecl != DynamicDecl) {
+  if (StaticDecl != DynamicDecl &&
+      !llvm::is_contained(S.InitializingBlocks, ThisPtr.block())) {
     if (!DynamicDecl->isDerivedFrom(StaticDecl))
       return false;
     Overrider = S.getContext().getOverridingFunction(DynamicDecl, StaticDecl,
@@ -1709,6 +1696,17 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
     Overrider = InitialFunction;
   }
 
+  // C++2a [class.abstract]p6:
+  //   the effect of making a virtual call to a pure virtual function [...] is
+  //   undefined
+  if (Overrider->isPureVirtual()) {
+    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_pure_virtual_call,
+             1)
+        << Callee;
+    S.Note(Callee->getLocation(), diag::note_declared_at);
+    return false;
+  }
+
   if (Overrider != InitialFunction) {
     // 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
diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp
index 1888998ebe3dd..cb788fa3e2c07 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1183,3 +1183,21 @@ namespace VirtualFunctionCallThroughArrayElem {
   static_assert(a[2][3].foo()); // both-error {{not an integral constant expression}} \
                                 // both-note {{virtual function called on object 'a[2][3]' whose dynamic type is not constant}}
 }
+
+namespace NonPureVirtualCall {
+  struct A {
+    constexpr virtual void call(int) = 0;
+    constexpr void call2() { call(0); }
+  };
+
+  struct B : A {
+    constexpr void call(int) override {}
+  };
+
+  consteval void check() {
+    B b;
+    b.call2();
+  }
+
+  int main() { check(); }
+}

@tbaederr tbaederr merged commit f49cd17 into llvm:main Oct 28, 2025
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:bytecode Issues for the clang bytecode constexpr interpreter clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[clang][bytecode] Call to consteval function is not a constant expression when calling a virtual method

2 participants