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
Lukacma pushed a commit to Lukacma/llvm-project that referenced this pull request Oct 29, 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 llvm#165234
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 30, 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 llvm#165234
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