[clang] 1fee6a1 - [clang][bytecode] Ignore GetPtrDerivedPop on non-record pointers (#194005)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 27 02:23:48 PDT 2026
Author: Timm Baeder
Date: 2026-04-27T11:23:43+02:00
New Revision: 1fee6a1e04de55ec218f9f38cbf2f60fc43acd26
URL: https://github.com/llvm/llvm-project/commit/1fee6a1e04de55ec218f9f38cbf2f60fc43acd26
DIFF: https://github.com/llvm/llvm-project/commit/1fee6a1e04de55ec218f9f38cbf2f60fc43acd26.diff
LOG: [clang][bytecode] Ignore GetPtrDerivedPop on non-record pointers (#194005)
Now that we do this for `GetPtrBase`, we need to do it here, too. This
broke in the attached test case, but fully fixing it requires some
seemingly unrelated changes to `delete` handling.
Added:
Modified:
clang/lib/AST/ByteCode/Interp.cpp
clang/lib/AST/ByteCode/Interp.h
clang/test/AST/ByteCode/new-delete.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 21a75b8fb4d45..729c0cc4ab541 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1300,7 +1300,7 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
// Remove base casts.
QualType InitialType = Ptr.getType();
- Ptr = Ptr.stripBaseCasts();
+ Ptr = Ptr.expand().stripBaseCasts();
Source = Ptr.getDeclDesc()->asExpr();
BlockToDelete = Ptr.block();
@@ -1515,11 +1515,50 @@ static bool getBase(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
const auto &Ptr = S.Stk.peek<Pointer>();
- return getBase(S, OpPC, Ptr, Off, /*NullOK=*/true);
+ return getBase(S, OpPC, Ptr.narrow(), Off, /*NullOK=*/true);
}
bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK) {
const auto &Ptr = S.Stk.pop<Pointer>();
- return getBase(S, OpPC, Ptr, Off, NullOK);
+ return getBase(S, OpPC, Ptr.narrow(), Off, NullOK);
+}
+
+bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK,
+ const Type *TargetType) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>().narrow();
+ if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Derived))
+ return false;
+
+ if (!Ptr.isBlockPointer()) {
+ // FIXME: We don't have the necessary information in integral pointers.
+ // The Descriptor only has a record, but that does of course not include
+ // the potential derived classes of said record.
+ S.Stk.push<Pointer>(Ptr);
+ return true;
+ }
+
+ if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
+ return false;
+ if (!CheckDowncast(S, OpPC, Ptr, Off))
+ return false;
+
+ if (!Ptr.getFieldDesc()->isRecord()) {
+ S.Stk.push<Pointer>(Ptr);
+ return true;
+ }
+
+ const Record *TargetRecord = Ptr.atFieldSub(Off).getRecord();
+ assert(TargetRecord);
+
+ if (TargetRecord->getDecl()->getCanonicalDecl() !=
+ TargetType->getAsCXXRecordDecl()->getCanonicalDecl()) {
+ QualType MostDerivedType = Ptr.getDeclDesc()->getType();
+ S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_downcast)
+ << MostDerivedType << QualType(TargetType, 0);
+ return false;
+ }
+
+ S.Stk.push<Pointer>(Ptr.atFieldSub(Off));
+ return true;
}
static bool checkConstructor(InterpState &S, CodePtr OpPC, const Function *Func,
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index b9d69f5be3c00..412a9289e424e 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1985,6 +1985,9 @@ bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off);
bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off);
bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK);
+bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK,
+ const Type *TargetType);
+
inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
if (S.checkingPotentialConstantExpression() && S.Current->getDepth() == 0)
return false;
@@ -1995,40 +1998,6 @@ inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
return true;
}
-inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off,
- bool NullOK, const Type *TargetType) {
- const Pointer &Ptr = S.Stk.pop<Pointer>();
- if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Derived))
- return false;
-
- if (!Ptr.isBlockPointer()) {
- // FIXME: We don't have the necessary information in integral pointers.
- // The Descriptor only has a record, but that does of course not include
- // the potential derived classes of said record.
- S.Stk.push<Pointer>(Ptr);
- return true;
- }
-
- if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
- return false;
- if (!CheckDowncast(S, OpPC, Ptr, Off))
- return false;
-
- const Record *TargetRecord = Ptr.atFieldSub(Off).getRecord();
- assert(TargetRecord);
-
- if (TargetRecord->getDecl()->getCanonicalDecl() !=
- TargetType->getAsCXXRecordDecl()->getCanonicalDecl()) {
- QualType MostDerivedType = Ptr.getDeclDesc()->getType();
- S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_downcast)
- << MostDerivedType << QualType(TargetType, 0);
- return false;
- }
-
- S.Stk.push<Pointer>(Ptr.atFieldSub(Off));
- return true;
-}
-
inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
if (S.checkingPotentialConstantExpression() && S.Current->isBottomFrame())
return false;
diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index 6467aa2e1d20b..2a04f28032b6c 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -1154,6 +1154,40 @@ namespace BrokenDelete {
}
}
+namespace vdtor {
+ constexpr int vdtor_3(int mode) {
+ int a = 0;
+ struct S { constexpr virtual ~S() {} };
+ struct T : S {
+ constexpr T(int *p) : p(p) {}
+ constexpr ~T() { ++*p; }
+ int *p;
+ };
+ S *p = new T[3]{&a, &a, &a}; // both-note 2{{heap allocation}} \
+ // both-note {{allocated with 'new[]' here}}
+ switch (mode) {
+ case 0:
+ delete p; // both-note {{non-array delete used to delete pointer to array object of type 'T[3]'}} \
+ // both-warning {{'delete' applied to a pointer that was allocated with 'new[]'}}
+ break;
+ case 1:
+ delete[] p; // both-note {{delete of pointer to subobject '&{*new T[3]#0}[0]'}}
+ break;
+ case 2:
+ delete (T*)p; // both-note {{non-array delete used to delete pointer to array object of type 'T[3]'}}
+ break;
+ case 3:
+ delete[] (T*)p;
+ break;
+ }
+ return a;
+ }
+ static_assert(vdtor_3(0) == 3); // both-error {{}} both-note {{in call}}
+ static_assert(vdtor_3(1) == 1); // both-error {{}} both-note {{in call}}
+ static_assert(vdtor_3(2) == 3); // both-error {{}} both-note {{in call}}
+ static_assert(vdtor_3(3) == 3);
+}
+
#else
/// Make sure we reject this prior to C++20
constexpr int a() { // both-error {{never produces a constant expression}}
More information about the cfe-commits
mailing list