PR5683: Issue a warning when subtracting pointers to types of zero size, and
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 10 Sep 2013 21:34:14 +0000 (21:34 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 10 Sep 2013 21:34:14 +0000 (21:34 +0000)
treat such subtractions as being non-constant. Patch by Serge Pavlov! With a
few tweaks by me.

llvm-svn: 190439

clang/include/clang/Basic/DiagnosticASTKinds.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/AST/ExprConstant.cpp
clang/lib/Sema/SemaExpr.cpp
clang/test/Sema/empty1.c
clang/test/SemaCXX/constant-expression-cxx11.cpp

index 86c7514..113e564 100644 (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">;
index 59542f0..e06560b 100644 (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">,
index b660214..218ce81 100644 (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
index da834cc..918201c 100644 (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();
     }
index d716ba5..de922f7 100644 (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}}
+}
index 6824909..363ca8c 100644 (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}}
+  }
+}