Marius =?utf-8?q?Dörner?= <[email protected]>,
Marius =?utf-8?q?Dörner?= <[email protected]>,
Marius =?utf-8?q?Dörner?= <[email protected]>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/[email protected]>


https://github.com/mariusdr updated 
https://github.com/llvm/llvm-project/pull/158502

>From 7c01c2b38737302d2a4310b40b99ab14002068a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marius=20D=C3=B6rner?= <[email protected]>
Date: Sun, 14 Sep 2025 21:03:57 +0200
Subject: [PATCH 1/4] [Clang][Interp] Assert on virtual func call from array
 elem

Fixes #152893.

An assert was raised when a constexpr virtual function was called from
an constexpr array element with -fexperimental-new-constant-interpreter
set.
---
 clang/lib/AST/ByteCode/Interp.cpp |  9 +++++++--
 clang/test/AST/ByteCode/cxx20.cpp | 22 ++++++++++++++++++++++
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 0f322f6ed42ac..94815975c1b35 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1666,10 +1666,15 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const 
Function *Func,
       TypePtr = TypePtr.getBase();
 
     QualType DynamicType = TypePtr.getType();
-    if (DynamicType->isPointerType() || DynamicType->isReferenceType())
+    if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
       DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
-    else
+    } else if (DynamicType->isArrayType()) {
+      const Type *ElemType = DynamicType->getPointeeOrArrayElementType();
+      assert(ElemType);
+      DynamicDecl = ElemType->getAsCXXRecordDecl();
+    } else {
       DynamicDecl = DynamicType->getAsCXXRecordDecl();
+    }
   }
   assert(DynamicDecl);
 
diff --git a/clang/test/AST/ByteCode/cxx20.cpp 
b/clang/test/AST/ByteCode/cxx20.cpp
index 67bf9a732d8b7..8e77a081e3e60 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1100,3 +1100,25 @@ namespace DiscardedTrivialCXXConstructExpr {
   constexpr int y = foo(12); // both-error {{must be initialized by a constant 
expression}} \
                              // both-note {{in call to}}
 }
+
+namespace VirtualFunctionCallThroughArrayElem {
+  struct X {
+    constexpr virtual int foo() const {
+      return 3;
+    }
+  };
+  constexpr X xs[5];
+  static_assert(xs[3].foo() == 3);
+
+  constexpr X xs2[1][2];
+  static_assert(xs2[0].foo() == 3); // both-error {{is not a structure or 
union}}
+  static_assert(xs2[0][0].foo() == 3);
+
+  struct Y: public X {
+    constexpr int foo() const override {
+      return 1;
+    }
+  };
+  constexpr Y ys[20];
+  static_assert(ys[12].foo() == static_cast<const X&>(ys[12]).foo());
+}

>From 95717b91294fa6b7744df68f46d67f858c872f14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marius=20D=C3=B6rner?= <[email protected]>
Date: Sun, 21 Sep 2025 11:34:35 +0200
Subject: [PATCH 2/4] Add diagnostic when dynamic type is not constant

+ test cases
---
 clang/lib/AST/ByteCode/Interp.cpp | 60 +++++++++++++++++++------------
 clang/lib/AST/ByteCode/Interp.h   |  2 +-
 clang/test/AST/ByteCode/cxx20.cpp | 20 +++++++++++
 3 files changed, 59 insertions(+), 23 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 94815975c1b35..e76b69df7c050 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -445,7 +445,8 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer 
&Ptr,
   return true;
 }
 
-bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
+                   bool NoDiag) {
   assert(Desc);
 
   const auto *D = Desc->asVarDecl();
@@ -470,7 +471,7 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const 
Descriptor *Desc) {
   }
 
   if (IsConstant) {
-    if (S.getLangOpts().CPlusPlus) {
+    if (S.getLangOpts().CPlusPlus && !NoDiag) {
       S.CCEDiag(S.Current->getLocation(OpPC),
                 S.getLangOpts().CPlusPlus11
                     ? diag::note_constexpr_ltor_non_constexpr
@@ -478,7 +479,7 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const 
Descriptor *Desc) {
                 1)
           << D << T;
       S.Note(D->getLocation(), diag::note_declared_at);
-    } else {
+    } else if (!NoDiag) {
       S.CCEDiag(S.Current->getLocation(OpPC));
     }
     return true;
@@ -493,16 +494,18 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const 
Descriptor *Desc) {
     return true;
   }
 
-  diagnoseNonConstVariable(S, OpPC, D);
+  if (!NoDiag)
+    diagnoseNonConstVariable(S, OpPC, D);
   return false;
 }
 
-static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                          bool NoDiag = false) {
   if (!Ptr.isStatic() || !Ptr.isBlockPointer())
     return true;
   if (!Ptr.getDeclID())
     return true;
-  return CheckConstant(S, OpPC, Ptr.getDeclDesc());
+  return CheckConstant(S, OpPC, Ptr.getDeclDesc(), NoDiag);
 }
 
 bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
@@ -1636,6 +1639,33 @@ bool Call(InterpState &S, CodePtr OpPC, const Function 
*Func,
   return true;
 }
 
+static bool GetDynamicDecl(InterpState &S, CodePtr OpPC, Pointer TypePtr,
+                           const CXXRecordDecl *&DynamicDecl) {
+  while (TypePtr.isBaseClass())
+    TypePtr = TypePtr.getBase();
+
+  QualType DynamicType = TypePtr.getType();
+  if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
+    DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
+  } else if (DynamicType->isArrayType()) {
+    const Type *ElemType = DynamicType->getPointeeOrArrayElementType();
+    assert(ElemType);
+    DynamicDecl = ElemType->getAsCXXRecordDecl();
+  } else {
+    DynamicDecl = DynamicType->getAsCXXRecordDecl();
+  }
+
+  if (!CheckConstant(S, OpPC, TypePtr, true)) {
+    const Expr *E = S.Current->getExpr(OpPC);
+    APValue V = TypePtr.toAPValue(S.getASTContext());
+    QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
+    S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
+        << AccessKinds::AK_MemberCall << V.getAsString(S.getASTContext(), TT);
+    return false;
+  }
+  return true;
+}
+
 bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
               uint32_t VarArgSize) {
   assert(Func->hasThisPointer());
@@ -1660,22 +1690,8 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const 
Function *Func,
   }
 
   const CXXRecordDecl *DynamicDecl = nullptr;
-  {
-    Pointer TypePtr = ThisPtr;
-    while (TypePtr.isBaseClass())
-      TypePtr = TypePtr.getBase();
-
-    QualType DynamicType = TypePtr.getType();
-    if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
-      DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
-    } else if (DynamicType->isArrayType()) {
-      const Type *ElemType = DynamicType->getPointeeOrArrayElementType();
-      assert(ElemType);
-      DynamicDecl = ElemType->getAsCXXRecordDecl();
-    } else {
-      DynamicDecl = DynamicType->getAsCXXRecordDecl();
-    }
-  }
+  if (!GetDynamicDecl(S, OpPC, ThisPtr, DynamicDecl))
+    return false;
   assert(DynamicDecl);
 
   const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl());
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index b3b4b998439cc..29a8340ed1725 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -78,7 +78,7 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
 bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
 
 /// Checks if the Descriptor is of a constexpr or const global variable.
-bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc);
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc, bool 
NoDiag = false);
 
 /// Checks if a pointer points to a mutable field.
 bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
diff --git a/clang/test/AST/ByteCode/cxx20.cpp 
b/clang/test/AST/ByteCode/cxx20.cpp
index 8e77a081e3e60..7dc11314cce4f 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1073,6 +1073,10 @@ namespace Virtual {
     virtual constexpr int f() { return 10; }
   };
 
+  K k;
+  static_assert(k.f() == 10); // both-error {{not an integral constant 
expression}} \
+                              // both-note {{virtual function called on object 
'k' whose dynamic type is not constant}}
+
   class L : public K {
   public:
     int b = f();
@@ -1083,6 +1087,18 @@ namespace Virtual {
   static_assert(l.a == 10);
   static_assert(l.b == 10);
   static_assert(l.c == 10);
+
+  struct M {
+    K& mk = k;
+  };
+  static_assert(M{}.mk.f() == 10); // both-error {{not an integral constant 
expression}} \
+                                   // both-note {{virtual function called on 
object 'k' whose dynamic type is not constant}}
+
+  struct N {
+    K* mk = &k;
+  };
+  static_assert(N{}.mk->f() == 10); // both-error {{not an integral constant 
expression}} \
+                                    // both-note {{virtual function called on 
object 'k' whose dynamic type is not constant}}
 }
 
 namespace DiscardedTrivialCXXConstructExpr {
@@ -1121,4 +1137,8 @@ namespace VirtualFunctionCallThroughArrayElem {
   };
   constexpr Y ys[20];
   static_assert(ys[12].foo() == static_cast<const X&>(ys[12]).foo());
+
+  X a[3][4];
+  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}}
 }

>From 57bc74e216114163dfa59ff982776d92f329dc5b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marius=20D=C3=B6rner?= <[email protected]>
Date: Sun, 21 Sep 2025 11:53:37 +0200
Subject: [PATCH 3/4] clang-format

---
 clang/lib/AST/ByteCode/Interp.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 29a8340ed1725..503cb73eedfef 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -78,7 +78,8 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
 bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
 
 /// Checks if the Descriptor is of a constexpr or const global variable.
-bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc, bool 
NoDiag = false);
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
+                   bool NoDiag = false);
 
 /// Checks if a pointer points to a mutable field.
 bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

>From 89cbeb7947c1b574bea3a53c3b261335cd1cdf6b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marius=20D=C3=B6rner?= <[email protected]>
Date: Fri, 3 Oct 2025 14:29:45 +0200
Subject: [PATCH 4/4] do not use `CheckConstant`, add test cases

---
 clang/lib/AST/ByteCode/Interp.cpp             | 36 ++++++++--------
 clang/lib/AST/ByteCode/Interp.h               |  3 +-
 clang/test/AST/ByteCode/cxx20.cpp             | 43 ++++++++++++++++++-
 .../SemaCXX/constant-expression-p2280r4.cpp   |  2 +-
 4 files changed, 62 insertions(+), 22 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index e76b69df7c050..03d5a4fc14e7d 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -445,8 +445,7 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer 
&Ptr,
   return true;
 }
 
-bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
-                   bool NoDiag) {
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
   assert(Desc);
 
   const auto *D = Desc->asVarDecl();
@@ -471,7 +470,7 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const 
Descriptor *Desc,
   }
 
   if (IsConstant) {
-    if (S.getLangOpts().CPlusPlus && !NoDiag) {
+    if (S.getLangOpts().CPlusPlus) {
       S.CCEDiag(S.Current->getLocation(OpPC),
                 S.getLangOpts().CPlusPlus11
                     ? diag::note_constexpr_ltor_non_constexpr
@@ -479,7 +478,7 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const 
Descriptor *Desc,
                 1)
           << D << T;
       S.Note(D->getLocation(), diag::note_declared_at);
-    } else if (!NoDiag) {
+    } else {
       S.CCEDiag(S.Current->getLocation(OpPC));
     }
     return true;
@@ -494,18 +493,16 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const 
Descriptor *Desc,
     return true;
   }
 
-  if (!NoDiag)
-    diagnoseNonConstVariable(S, OpPC, D);
+  diagnoseNonConstVariable(S, OpPC, D);
   return false;
 }
 
-static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
-                          bool NoDiag = false) {
+static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
   if (!Ptr.isStatic() || !Ptr.isBlockPointer())
     return true;
   if (!Ptr.getDeclID())
     return true;
-  return CheckConstant(S, OpPC, Ptr.getDeclDesc(), NoDiag);
+  return CheckConstant(S, OpPC, Ptr.getDeclDesc());
 }
 
 bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
@@ -1645,6 +1642,18 @@ static bool GetDynamicDecl(InterpState &S, CodePtr OpPC, 
Pointer TypePtr,
     TypePtr = TypePtr.getBase();
 
   QualType DynamicType = TypePtr.getType();
+  if (TypePtr.isStatic() || TypePtr.isConst()) {
+    const VarDecl *VD = TypePtr.getDeclDesc()->asVarDecl();
+    if (!VD->isConstexpr()) {
+      const Expr *E = S.Current->getExpr(OpPC);
+      APValue V = TypePtr.toAPValue(S.getASTContext());
+      QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
+      S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
+          << AccessKinds::AK_MemberCall << V.getAsString(S.getASTContext(), 
TT);
+      return false;
+    }
+  }
+
   if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
     DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
   } else if (DynamicType->isArrayType()) {
@@ -1654,15 +1663,6 @@ static bool GetDynamicDecl(InterpState &S, CodePtr OpPC, 
Pointer TypePtr,
   } else {
     DynamicDecl = DynamicType->getAsCXXRecordDecl();
   }
-
-  if (!CheckConstant(S, OpPC, TypePtr, true)) {
-    const Expr *E = S.Current->getExpr(OpPC);
-    APValue V = TypePtr.toAPValue(S.getASTContext());
-    QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
-    S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
-        << AccessKinds::AK_MemberCall << V.getAsString(S.getASTContext(), TT);
-    return false;
-  }
   return true;
 }
 
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 503cb73eedfef..b3b4b998439cc 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -78,8 +78,7 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
 bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
 
 /// Checks if the Descriptor is of a constexpr or const global variable.
-bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
-                   bool NoDiag = false);
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc);
 
 /// Checks if a pointer points to a mutable field.
 bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
diff --git a/clang/test/AST/ByteCode/cxx20.cpp 
b/clang/test/AST/ByteCode/cxx20.cpp
index 7dc11314cce4f..1888998ebe3dd 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1070,13 +1070,30 @@ namespace Virtual {
   public:
     int a = f();
 
-    virtual constexpr int f() { return 10; }
+    virtual constexpr int f() const { return 10; }
   };
 
   K k;
   static_assert(k.f() == 10); // both-error {{not an integral constant 
expression}} \
                               // both-note {{virtual function called on object 
'k' whose dynamic type is not constant}}
 
+  void f() {
+    constexpr K k;
+    static_assert(k.f() == 10);
+  }
+
+  void f2() {
+    K k;
+    static_assert(k.f() == 10); // both-error {{not an integral constant 
expression}} \
+                                // both-note {{virtual function called on 
object 'k' whose dynamic type is not constant}}
+  }
+  
+  static_assert(K().f() == 10);
+
+  void f3() {
+    static_assert(K().f() == 10);
+  }
+
   class L : public K {
   public:
     int b = f();
@@ -1087,6 +1104,7 @@ namespace Virtual {
   static_assert(l.a == 10);
   static_assert(l.b == 10);
   static_assert(l.c == 10);
+  static_assert(l.f() == 10);
 
   struct M {
     K& mk = k;
@@ -1099,6 +1117,29 @@ namespace Virtual {
   };
   static_assert(N{}.mk->f() == 10); // both-error {{not an integral constant 
expression}} \
                                     // both-note {{virtual function called on 
object 'k' whose dynamic type is not constant}}
+
+  extern K o;
+  static_assert(o.f() == 10); // both-error {{not an integral constant 
expression}} \
+                              // both-note {{virtual function called on object 
'o' whose dynamic type is not constant}}
+  static K p;
+  static_assert(p.f() == 10); // both-error {{not an integral constant 
expression}} \
+                              // both-note {{virtual function called on object 
'p' whose dynamic type is not constant}}
+  
+  void f4() {
+    static K p;
+    static_assert(p.f() == 10); // both-error {{not an integral constant 
expression}} \
+                                // both-note {{virtual function called on 
object 'p' whose dynamic type is not constant}}
+  }
+  
+  const K q;
+  static_assert(q.f() == 10); // both-error {{not an integral constant 
expression}} \
+                              // both-note {{virtual function called on object 
'q' whose dynamic type is not constant}}
+
+  void f5() {
+    const K q;
+    static_assert(q.f() == 10); // both-error {{not an integral constant 
expression}} \
+                                // both-note {{virtual function called on 
object 'q' whose dynamic type is not constant}}
+  }
 }
 
 namespace DiscardedTrivialCXXConstructExpr {
diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp 
b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
index 78e2e17016280..5cbfaffd04aa0 100644
--- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -44,7 +44,7 @@ void splash(Swim& swam) {                 // 
nointerpreter-note {{declared here}
   static_assert(how_many(swam) == 28);    // ok
   static_assert(Swim().lochte() == 12);   // ok
   static_assert(swam.lochte() == 12);     // expected-error {{static assertion 
expression is not an integral constant expression}} \
-                                          // nointerpreter-note {{virtual 
function called on object 'swam' whose dynamic type is not constant}}
+                                          // expected-note {{virtual function 
called on object 'swam' whose dynamic type is not constant}}
   static_assert(swam.coughlin == 12);     // expected-error {{static assertion 
expression is not an integral constant expression}} \
                                           // nointerpreter-note {{read of 
variable 'swam' whose value is not known}}
 }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to