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
