PR5683: Issue a warning when subtracting pointers to types of zero size, and

treat such subtractions as being non-constant. Patch by Serge Pavlov! With a
few tweaks by me.

llvm-svn: 190439
This commit is contained in:
Richard Smith 2013-09-10 21:34:14 +00:00
parent 754a3ead62
commit 84c6b3d293
6 changed files with 86 additions and 1 deletions

View File

@ -46,6 +46,8 @@ def note_constexpr_float_arithmetic : Note<
"floating point arithmetic produces %select{an infinity|a NaN}0">;
def note_constexpr_pointer_subtraction_not_same_array : Note<
"subtracted pointers are not elements of the same array">;
def note_constexpr_pointer_subtraction_zero_size : Note<
"subtraction of pointers to type %0 of zero size">;
def note_constexpr_pointer_comparison_base_classes : Note<
"comparison of addresses of subobjects of different base classes "
"has unspecified value">;

View File

@ -4191,6 +4191,9 @@ def warn_offsetof_non_pod_type : ExtWarn<"offset of on non-POD type %0">,
def warn_offsetof_non_standardlayout_type : ExtWarn<
"offset of on non-standard-layout type %0">, InGroup<InvalidOffsetof>;
def err_offsetof_bitfield : Error<"cannot compute offset of bit-field %0">;
def warn_sub_ptr_zero_size_types : Warning<
"subtraction of pointers to type %0 of zero size has undefined behavior">,
InGroup<PointerArith>;
def warn_floatingpoint_eq : Warning<
"comparing floating point with == or != is unsafe">,

View File

@ -6570,6 +6570,15 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
if (!HandleSizeof(Info, E->getExprLoc(), ElementType, ElementSize))
return false;
// As an extension, a type may have zero size (empty struct or union in
// C, array of zero length). Pointer subtraction in such cases has
// undefined behavior, so is not constant.
if (ElementSize.isZero()) {
Info.Diag(E, diag::note_constexpr_pointer_subtraction_zero_size)
<< ElementType;
return false;
}
// FIXME: LLVM and GCC both compute LHSOffset - RHSOffset at runtime,
// and produce incorrect results when it overflows. Such behavior
// appears to be non-conforming, but is common, so perhaps we should

View File

@ -7041,6 +7041,18 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
LHS.get(), RHS.get()))
return QualType();
// The pointee type may have zero size. As an extension, a structure or
// union may have zero size or an array may have zero length. In this
// case subtraction does not make sense.
if (!rpointee->isVoidType() && !rpointee->isFunctionType()) {
CharUnits ElementSize = Context.getTypeSizeInChars(rpointee);
if (ElementSize.isZero()) {
Diag(Loc,diag::warn_sub_ptr_zero_size_types)
<< rpointee.getUnqualifiedType()
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
}
}
if (CompLHSTy) *CompLHSTy = LHS.get()->getType();
return Context.getPointerDiffType();
}

View File

@ -36,3 +36,50 @@ struct emp_9 { // expected-warning {{struct has size 0 in C, non-zero size in C+
struct emp_1 f1;
union emp_2 f2;
};
// Checks for pointer subtraction (PR15683)
struct emp_1 *func_1p(struct emp_1 *x) { return x - 5; }
int func_1() {
struct emp_1 v[1];
return v - v; // expected-warning {{subtraction of pointers to type 'struct emp_1' of zero size has undefined behavior}}
}
int func_2(struct emp_1 *x) {
return 1 + x - x; // expected-warning {{subtraction of pointers to type 'struct emp_1' of zero size has undefined behavior}}
}
int func_3(struct emp_1 *x, struct emp_1 *y) {
return x - y; // expected-warning {{subtraction of pointers to type 'struct emp_1' of zero size has undefined behavior}}
}
int func_4(struct emp_1 *x, const struct emp_1 *y) {
return x - y; // expected-warning {{subtraction of pointers to type 'struct emp_1' of zero size has undefined behavior}}
}
int func_5(volatile struct emp_1 *x, const struct emp_1 *y) {
return x - y; // expected-warning {{subtraction of pointers to type 'struct emp_1' of zero size has undefined behavior}}
}
int func_6() {
union emp_2 v[1];
return v - v; // expected-warning {{subtraction of pointers to type 'union emp_2' of zero size has undefined behavior}}
}
struct A; // expected-note {{forward declaration of 'struct A'}}
int func_7(struct A *x, struct A *y) {
return x - y; // expected-error {{arithmetic on a pointer to an incomplete type 'struct A'}}
}
int func_8(struct emp_1 (*x)[10], struct emp_1 (*y)[10]) {
return x - y; // expected-warning {{subtraction of pointers to type 'struct emp_1 [10]' of zero size has undefined behavior}}
}
int func_9(struct emp_1 (*x)[], struct emp_1 (*y)[]) {
return x - y; // expected-error {{arithmetic on a pointer to an incomplete type 'struct emp_1 []'}}
}
int func_10(int (*x)[0], int (*y)[0]) {
return x - y; // expected-warning {{subtraction of pointers to type 'int [0]' of zero size has undefined behavior}}
}

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -triple i686-linux -Wno-string-plus-int -fsyntax-only -fcxx-exceptions -verify -std=c++11 -pedantic %s -Wno-comment
// RUN: %clang_cc1 -triple i686-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -fsyntax-only -fcxx-exceptions -verify -std=c++11 -pedantic %s -Wno-comment
namespace StaticAssertFoldTest {
@ -1764,3 +1764,15 @@ namespace Bitfields {
static_assert(X::f(3) == -1, "3 should truncate to -1");
}
}
namespace ZeroSizeTypes {
constexpr int (*p1)[0] = 0, (*p2)[0] = 0;
constexpr int k = p2 - p1;
// expected-error@-1 {{constexpr variable 'k' must be initialized by a constant expression}}
// expected-note@-2 {{subtraction of pointers to type 'int [0]' of zero size}}
int arr[5][0];
constexpr int f() { // expected-error {{never produces a constant expression}}
return &arr[3] - &arr[0]; // expected-note {{subtraction of pointers to type 'int [0]' of zero size}}
}
}