From 812d6bcbd13190e6e5c2c915bf1499038d56b44b Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 10 Sep 2013 21:34:14 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@190439 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticASTKinds.td | 2 + include/clang/Basic/DiagnosticSemaKinds.td | 3 ++ lib/AST/ExprConstant.cpp | 9 +++++ lib/Sema/SemaExpr.cpp | 12 ++++++ test/Sema/empty1.c | 47 ++++++++++++++++++++++ test/SemaCXX/constant-expression-cxx11.cpp | 14 ++++++- 6 files changed, 86 insertions(+), 1 deletion(-) diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 86c7514593..113e564905 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -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">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 59542f0071..e06560b55a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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; 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; def warn_floatingpoint_eq : Warning< "comparing floating point with == or != is unsafe">, diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index b6602145e2..218ce8101b 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -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 diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index da834cc251..918201c44c 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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(); } diff --git a/test/Sema/empty1.c b/test/Sema/empty1.c index d716ba52bb..de922f775e 100644 --- a/test/Sema/empty1.c +++ b/test/Sema/empty1.c @@ -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}} +} diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 6824909db8..363ca8c7d9 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -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}} + } +} -- 2.40.0