]> granicus.if.org Git - clang/commitdiff
Implement correct semantic analysis of subtractions, implementing
authorChris Lattner <sabre@nondot.org>
Sun, 9 Dec 2007 21:53:25 +0000 (21:53 +0000)
committerChris Lattner <sabre@nondot.org>
Sun, 9 Dec 2007 21:53:25 +0000 (21:53 +0000)
C99 6.5.6.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@44746 91177308-0d34-0410-b5e6-96231b3b80d8

Sema/SemaExpr.cpp
include/clang/Basic/DiagnosticKinds.def
test/Sema/typecheck-binop.c [new file with mode: 0644]

index 8c5ef43f4c5638173c66c019844144861f2715f8..3fa159eb4463ad25aeb8cfea08ac48cd9eeb0b30 100644 (file)
@@ -1259,14 +1259,60 @@ inline QualType Sema::CheckSubtractionOperands( // C99 6.5.6
     
   QualType compType = UsualArithmeticConversions(lex, rex, isCompAssign);
   
-  // handle the common case first (both operands are arithmetic).
+  // Enforce type constraints: C99 6.5.6p3.
+  
+  // Handle the common case first (both operands are arithmetic).
   if (lex->getType()->isArithmeticType() && rex->getType()->isArithmeticType())
     return compType;
+  
+  // Either ptr - int   or   ptr - ptr.
+  if (const PointerType *LHSPTy = lex->getType()->getAsPointerType()) {
+    // The LHS must be an object type, not incomplete, function, etc.
+    if (!LHSPTy->getPointeeType()->isObjectType()) {
+      // Handle the GNU void* extension.
+      if (LHSPTy->getPointeeType()->isVoidType()) {
+        Diag(loc, diag::ext_gnu_void_ptr, 
+             lex->getSourceRange(), rex->getSourceRange());
+      } else {
+        Diag(loc, diag::err_typecheck_sub_ptr_object,
+             lex->getType().getAsString(), lex->getSourceRange());
+        return QualType();
+      }
+    }
+
+    // The result type of a pointer-int computation is the pointer type.
+    if (rex->getType()->isIntegerType())
+      return lex->getType();
     
-  if (lex->getType()->isPointerType() && rex->getType()->isIntegerType())
-    return compType;
-  if (lex->getType()->isPointerType() && rex->getType()->isPointerType())
-    return Context.getPointerDiffType();
+    // Handle pointer-pointer subtractions.
+    if (const PointerType *RHSPTy = rex->getType()->getAsPointerType()) {
+      // RHS must be an object type, unless void (GNU).
+      if (!RHSPTy->getPointeeType()->isObjectType()) {
+        // Handle the GNU void* extension.
+        if (RHSPTy->getPointeeType()->isVoidType()) {
+          if (!LHSPTy->getPointeeType()->isVoidType())
+            Diag(loc, diag::ext_gnu_void_ptr, 
+                 lex->getSourceRange(), rex->getSourceRange());
+        } else {
+          Diag(loc, diag::err_typecheck_sub_ptr_object,
+               rex->getType().getAsString(), rex->getSourceRange());
+          return QualType();
+        }
+      }
+      
+      // Pointee types must be compatible.
+      if (!Context.typesAreCompatible(LHSPTy->getPointeeType(), 
+                                      RHSPTy->getPointeeType())) {
+        Diag(loc, diag::err_typecheck_sub_ptr_compatible,
+             lex->getType().getAsString(), rex->getType().getAsString(),
+             lex->getSourceRange(), rex->getSourceRange());
+        return QualType();
+      }
+      
+      return Context.getPointerDiffType();
+    }
+  }
+  
   InvalidOperands(loc, lex, rex);
   return QualType();
 }
index 043dcd4f34e2eca57813c736509d5b23c09bc36a..e37262f93eb5df784a5122018ffabbe5e1d9cc9b 100644 (file)
@@ -297,7 +297,9 @@ DIAG(ext_c99_compound_literal, EXTENSION,
      "compound literals are a C99-specific feature")
 DIAG(ext_c99_enumerator_list_comma, EXTENSION,
      "commas at the end of enumerator lists are a C99-specific feature")
-     
+
+DIAG(ext_gnu_void_ptr, EXTENSION,
+     "use of GNU void* extension")
 DIAG(ext_gnu_indirect_goto, EXTENSION,
      "use of GNU indirect-goto extension")
 DIAG(ext_gnu_address_of_label, EXTENSION,
@@ -733,6 +735,10 @@ DIAG(ext_typecheck_deref_ptr_to_void, WARNING,
      "dereferencing '%0' pointer")
 DIAG(err_typecheck_invalid_operands, ERROR,
      "invalid operands to binary expression ('%0' and '%1')")
+DIAG(err_typecheck_sub_ptr_object, ERROR,
+     "'%0' is not a complete object type")
+DIAG(err_typecheck_sub_ptr_compatible, ERROR,
+     "'%0' and '%1' are not pointers to compatible types")
 DIAG(ext_typecheck_comparison_of_pointer_integer, WARNING,
      "comparison between pointer and integer ('%0' and '%1')")
 DIAG(ext_typecheck_comparison_of_distinct_pointers, WARNING,
diff --git a/test/Sema/typecheck-binop.c b/test/Sema/typecheck-binop.c
new file mode 100644 (file)
index 0000000..05b9ad9
--- /dev/null
@@ -0,0 +1,20 @@
+/* RUN: clang %s -fsyntax-only -pedantic -verify
+ */
+struct incomplete;
+
+int sub1(int *a, double *b) { 
+  return a - b;    /* expected-error{{not pointers to compatible types}} */
+}
+
+void *sub2(struct incomplete *P) {
+  return P-4;      /* expected-error{{not a complete object type}} */
+}
+
+void *sub3(void *P) {
+  return P-4;      /* expected-warning{{GNU void* extension}} */
+}
+
+int sub4(void *P, void *Q) {
+  return P-Q;      /* expected-warning{{GNU void* extension}} */
+}
+