]> granicus.if.org Git - clang/commitdiff
Implement -Wcast-align. The initial design of this diagnostic diverges
authorJohn McCall <rjmccall@apple.com>
Thu, 12 Aug 2010 21:44:57 +0000 (21:44 +0000)
committerJohn McCall <rjmccall@apple.com>
Thu, 12 Aug 2010 21:44:57 +0000 (21:44 +0000)
from GCC's in that we warn on *any* increase in alignment requirements, not
just those that are enforced by hardware.  Please let us know if this causes
major problems for you (which it shouldn't, since it's an optional warning).

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

include/clang/Basic/DiagnosticGroups.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Sema/SemaCXXCast.cpp
lib/Sema/SemaChecking.cpp
lib/Sema/SemaExpr.cpp
test/Sema/warn-cast-align.c [new file with mode: 0644]
test/SemaCXX/warn-cast-align.cpp [new file with mode: 0644]

index 6849349a91bcba040f60a59d68d1f2ee1245266d..f0ad0ab4371289e66ca27db7c2cf796d25305b23 100644 (file)
@@ -26,7 +26,7 @@ def : DiagGroup<"attributes">;
 def : DiagGroup<"bad-function-cast">;
 def BoolConversions : DiagGroup<"bool-conversions">;
 def CXXCompat: DiagGroup<"c++-compat">;
-def : DiagGroup<"cast-align">;
+def CastAlign : DiagGroup<"cast-align">;
 def : DiagGroup<"cast-qual">;
 def : DiagGroup<"char-align">;
 def Comment : DiagGroup<"comment">;
index a9e976f434ace0337932e58c25c67ebd9dd1000c..9663acc6a3c2003e69700b20a7502c72233cb21d 100644 (file)
@@ -971,6 +971,10 @@ def warn_impcast_integer_64_32 : Warning<
   "implicit conversion loses integer precision: %0 to %1">,
   InGroup<DiagGroup<"shorten-64-to-32">>, DefaultIgnore;
 
+def warn_cast_align : Warning<
+  "cast from %0 to %1 increases required alignment from %2 to %3">,
+  InGroup<CastAlign>, DefaultIgnore;
+
 def warn_attribute_ignored_for_field_of_type : Warning<
   "%0 attribute ignored for field of type %1">;
 def warn_transparent_union_attribute_field_size_align : Warning<
index 0d7ac70881a3e95fddeab07558dab05112c77eaf..f3d15657a17ecf7f856482f330ba88995265bd5e 100644 (file)
@@ -860,6 +860,7 @@ public:
   void DiagnoseFunctionSpecifiers(Declarator& D);
   void CheckShadow(Scope *S, VarDecl *D, const LookupResult& R);
   void CheckShadow(Scope *S, VarDecl *D);
+  void CheckCastAlign(Expr *Op, QualType T, SourceRange TRange);
   NamedDecl* ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC,
                                     QualType R, TypeSourceInfo *TInfo,
                                     LookupResult &Previous, bool &Redeclaration);
index 5753e7bce47844b2aa5d859f1cc36be6a1b23854..59df294772e809e15e69c2aca135fb203c9d7db1 100644 (file)
@@ -468,6 +468,8 @@ CheckReinterpretCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
       != TC_Success && msg != 0)
     Self.Diag(OpRange.getBegin(), msg) << CT_Reinterpret
       << SrcExpr->getType() << DestType << OpRange;
+  else if (Kind == CastExpr::CK_Unknown || Kind == CastExpr::CK_BitCast)
+    Self.CheckCastAlign(SrcExpr, DestType, OpRange);
 }
 
 
@@ -494,6 +496,8 @@ CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
                     Kind, BasePath) != TC_Success && msg != 0)
     Self.Diag(OpRange.getBegin(), msg) << CT_Static
       << SrcExpr->getType() << DestType << OpRange;
+  else if (Kind == CastExpr::CK_Unknown || Kind == CastExpr::CK_BitCast)
+    Self.CheckCastAlign(SrcExpr, DestType, OpRange);
 }
 
 /// TryStaticCast - Check if a static cast can be performed, and do so if
@@ -1303,6 +1307,8 @@ Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, Expr *&CastExpr,
   if (tcr != TC_Success && msg != 0)
     Diag(R.getBegin(), msg) << (FunctionalStyle ? CT_Functional : CT_CStyle)
       << CastExpr->getType() << CastTy << R;
+  else if (Kind == CastExpr::CK_Unknown || Kind == CastExpr::CK_BitCast)
+    CheckCastAlign(CastExpr, CastTy, R);
 
   return tcr != TC_Success;
 }
index 7e1eebebdbf295e07c148dcd51243856fc1b38c9..14c0d87ffb7f91fd0103926b0ecc42a3a2cfb01a 100644 (file)
@@ -2796,3 +2796,48 @@ bool Sema::CheckParmsForFunctionDef(FunctionDecl *FD) {
 
   return HasInvalidParm;
 }
+
+/// CheckCastAlign - Implements -Wcast-align, which warns when a
+/// pointer cast increases the alignment requirements.
+void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) {
+  // This is actually a lot of work to potentially be doing on every
+  // cast; don't do it if we're ignoring -Wcast_align (as is the default).
+  if (getDiagnostics().getDiagnosticLevel(diag::warn_cast_align)
+        == Diagnostic::Ignored)
+    return;
+
+  // Ignore dependent types.
+  if (T->isDependentType() || Op->getType()->isDependentType())
+    return;
+
+  // Require that the destination be a pointer type.
+  const PointerType *DestPtr = T->getAs<PointerType>();
+  if (!DestPtr) return;
+
+  // If the destination has alignment 1, we're done.
+  QualType DestPointee = DestPtr->getPointeeType();
+  if (DestPointee->isIncompleteType()) return;
+  CharUnits DestAlign = Context.getTypeAlignInChars(DestPointee);
+  if (DestAlign.isOne()) return;
+
+  // Require that the source be a pointer type.
+  const PointerType *SrcPtr = Op->getType()->getAs<PointerType>();
+  if (!SrcPtr) return;
+  QualType SrcPointee = SrcPtr->getPointeeType();
+
+  // Whitelist casts from cv void*.  We already implicitly
+  // whitelisted casts to cv void*, since they have alignment 1.
+  // Also whitelist casts involving incomplete types, which implicitly
+  // includes 'void'.
+  if (SrcPointee->isIncompleteType()) return;
+
+  CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee);
+  if (SrcAlign >= DestAlign) return;
+
+  Diag(TRange.getBegin(), diag::warn_cast_align)
+    << Op->getType() << T
+    << static_cast<unsigned>(SrcAlign.getQuantity())
+    << static_cast<unsigned>(DestAlign.getQuantity())
+    << TRange << Op->getSourceRange();
+}
+
index 8643dd8386d3c9f6bf2c31ce2ec3049d6e83d14c..f87d39fd8f00321819b48dd07e1dabbc3994da60 100644 (file)
@@ -3995,6 +3995,10 @@ bool Sema::CheckCastTypes(SourceRange TyR, QualType castType, Expr *&castExpr,
   }
 
   Kind = getScalarCastKind(Context, castExpr->getType(), castType);
+
+  if (Kind == CastExpr::CK_Unknown || Kind == CastExpr::CK_BitCast)
+    CheckCastAlign(castExpr, castType, TyR);
+
   return false;
 }
 
diff --git a/test/Sema/warn-cast-align.c b/test/Sema/warn-cast-align.c
new file mode 100644 (file)
index 0000000..11e3c41
--- /dev/null
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -Wcast-align -verify %s
+
+// Simple casts.
+void test0(char *P) {
+  char *a  = (char*)  P;
+  short *b = (short*) P; // expected-warning {{cast from 'char *' to 'short *' increases required alignment from 1 to 2}}
+  int *c   = (int*)   P; // expected-warning {{cast from 'char *' to 'int *' increases required alignment from 1 to 4}}
+}
+
+// Casts from void* are a special case.
+void test1(void *P) {
+  char *a  = (char*)  P;
+  short *b = (short*) P;
+  int *c   = (int*)   P;
+
+  const volatile void *P2 = P;
+  char *d  = (char*)  P2;
+  short *e = (short*) P2;
+  int *f   = (int*)   P2;
+
+  const char *g  = (const char*)  P2;
+  const short *h = (const short*) P2;
+  const int *i   = (const int*)   P2;
+
+  const volatile char *j  = (const volatile char*)  P2;
+  const volatile short *k = (const volatile short*) P2;
+  const volatile int *l   = (const volatile int*)   P2;
+}
+
+// Aligned struct.
+__attribute__((align(16))) struct A {
+  char buffer[16];
+};
+void test2(char *P) {
+  struct A *a = (struct A*) P; // expected-warning {{cast from 'char *' to 'struct A *' increases required alignment from 1 to 16}}
+}
+
+// Incomplete type.
+void test3(char *P) {
+  struct B *b = (struct B*) P;
+}
diff --git a/test/SemaCXX/warn-cast-align.cpp b/test/SemaCXX/warn-cast-align.cpp
new file mode 100644 (file)
index 0000000..d2144d2
--- /dev/null
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -Wcast-align -verify %s
+
+// Simple casts.
+void test0(char *P) {
+  char *a; short *b; int *c;
+
+  a = (char*) P;
+  a = static_cast<char*>(P);
+  a = reinterpret_cast<char*>(P);
+  typedef char *CharPtr;
+  a = CharPtr(P);
+
+  b = (short*) P; // expected-warning {{cast from 'char *' to 'short *' increases required alignment from 1 to 2}}
+  b = reinterpret_cast<short*>(P); // expected-warning {{cast from 'char *' to 'short *' increases required alignment from 1 to 2}}
+  typedef short *ShortPtr;
+  b = ShortPtr(P); // expected-warning {{cast from 'char *' to 'ShortPtr' (aka 'short *') increases required alignment from 1 to 2}}
+
+  c = (int*) P; // expected-warning {{cast from 'char *' to 'int *' increases required alignment from 1 to 4}}
+  c = reinterpret_cast<int*>(P); // expected-warning {{cast from 'char *' to 'int *' increases required alignment from 1 to 4}}
+  typedef int *IntPtr;
+  c = IntPtr(P); // expected-warning {{cast from 'char *' to 'IntPtr' (aka 'int *') increases required alignment from 1 to 4}}
+}
+
+// Casts from void* are a special case.
+void test1(void *P) {
+  char *a; short *b; int *c;
+
+  a = (char*) P;
+  a = static_cast<char*>(P);
+  a = reinterpret_cast<char*>(P);
+  typedef char *CharPtr;
+  a = CharPtr(P);
+
+  b = (short*) P;
+  b = static_cast<short*>(P);
+  b = reinterpret_cast<short*>(P);
+  typedef short *ShortPtr;
+  b = ShortPtr(P);
+
+  c = (int*) P;
+  c = static_cast<int*>(P);
+  c = reinterpret_cast<int*>(P);
+  typedef int *IntPtr;
+  c = IntPtr(P);
+}