forked from OSchip/llvm-project
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:
parent
754a3ead62
commit
84c6b3d293
|
@ -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">;
|
||||
|
|
|
@ -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">,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue