diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 1243380ca8a6b..dfc6250afedd7 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -1039,8 +1039,15 @@ bool Compiler::VisitPointerArithBinOp(const BinaryOperator *E) { if (!visitAsPointer(RHS, *RT) || !visitAsPointer(LHS, *LT)) return false; + QualType ElemType = LHS->getType()->getPointeeType(); + CharUnits ElemTypeSize; + if (ElemType->isVoidType() || ElemType->isFunctionType()) + ElemTypeSize = CharUnits::One(); + else + ElemTypeSize = Ctx.getASTContext().getTypeSizeInChars(ElemType); + PrimType IntT = classifyPrim(E->getType()); - if (!this->emitSubPtr(IntT, E)) + if (!this->emitSubPtr(IntT, ElemTypeSize.isZero(), E)) return false; return DiscardResult ? this->emitPop(IntT, E) : true; } diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index cbd60c9f2b37c..19c8d6d850339 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -2390,7 +2390,7 @@ static inline bool DecPtr(InterpState &S, CodePtr OpPC) { /// 2) Pops another Pointer from the stack. /// 3) Pushes the difference of the indices of the two pointers on the stack. template ::T> -inline bool SubPtr(InterpState &S, CodePtr OpPC) { +inline bool SubPtr(InterpState &S, CodePtr OpPC, bool ElemSizeIsZero) { const Pointer &LHS = S.Stk.pop(); const Pointer &RHS = S.Stk.pop(); @@ -2402,25 +2402,23 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC) { return false; } - if (LHS == RHS) { - S.Stk.push(); - return true; - } + if (ElemSizeIsZero) { + QualType PtrT = LHS.getType(); + while (auto *AT = dyn_cast(PtrT)) + PtrT = AT->getElementType(); - for (const Pointer &P : {LHS, RHS}) { - if (P.isZeroSizeArray()) { - QualType PtrT = P.getType(); - while (auto *AT = dyn_cast(PtrT)) - PtrT = AT->getElementType(); + QualType ArrayTy = S.getASTContext().getConstantArrayType( + PtrT, APInt::getZero(1), nullptr, ArraySizeModifier::Normal, 0); + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_pointer_subtraction_zero_size) + << ArrayTy; - QualType ArrayTy = S.getASTContext().getConstantArrayType( - PtrT, APInt::getZero(1), nullptr, ArraySizeModifier::Normal, 0); - S.FFDiag(S.Current->getSource(OpPC), - diag::note_constexpr_pointer_subtraction_zero_size) - << ArrayTy; + return false; + } - return false; - } + if (LHS == RHS) { + S.Stk.push(); + return true; } int64_t A64 = diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 1785fcf4a7b20..ddf1a8fcc98b1 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -543,6 +543,7 @@ def SubOffset : AluOpcode; // [Pointer, Pointer] -> [Integral] def SubPtr : Opcode { let Types = [IntegerTypeClass]; + let Args = [ArgBool]; let HasGroup = 1; } diff --git a/clang/test/AST/ByteCode/arrays.cpp b/clang/test/AST/ByteCode/arrays.cpp index eaf9559e40cda..d83ae97fc8213 100644 --- a/clang/test/AST/ByteCode/arrays.cpp +++ b/clang/test/AST/ByteCode/arrays.cpp @@ -731,6 +731,10 @@ namespace ZeroSizeTypes { // both-note {{subtraction of pointers to type 'int[0]' of zero size}} \ // both-warning {{subtraction of pointers to type 'int[0]' of zero size has undefined behavior}} + constexpr int k2 = p1 - p1; // both-error {{constexpr variable 'k2' must be initialized by a constant expression}} \ + // both-note {{subtraction of pointers to type 'int[0]' of zero size}} \ + // both-warning {{subtraction of pointers to type 'int[0]' of zero size has undefined behavior}} + int arr[5][0]; constexpr int f() { // both-error {{never produces a constant expression}} return &arr[3] - &arr[0]; // both-note {{subtraction of pointers to type 'int[0]' of zero size}} \ diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp index f54854070573c..9e0f33e212c18 100644 --- a/clang/test/AST/ByteCode/new-delete.cpp +++ b/clang/test/AST/ByteCode/new-delete.cpp @@ -1104,6 +1104,16 @@ namespace HugeAllocation { } #endif +namespace ZeroSizeArray { + constexpr int foo() { + int *A = new int[0]; + int diff = A - (&A[0]); + delete[] A; + return diff; + } + static_assert(foo() == 0); +} + #else /// Make sure we reject this prior to C++20 constexpr int a() { // both-error {{never produces a constant expression}}