]> granicus.if.org Git - clang/commitdiff
Add a warning for when reinterpret_cast leads to undefined behavior, patch by Richard...
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Mon, 2 May 2011 18:21:19 +0000 (18:21 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Mon, 2 May 2011 18:21:19 +0000 (18:21 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130703 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Sema/SemaCXXCast.cpp
lib/Sema/SemaExpr.cpp
test/SemaCXX/reinterpret-cast.cpp

index 0a0c91ac9144da33b57ade799bf6860c11aeb205..aa7f64a24668f9b5d5eb695b9b2d7a560339f626 100644 (file)
@@ -2641,6 +2641,10 @@ def warn_indirection_through_null : Warning<
   "indirection of non-volatile null pointer will be deleted, not trap">, InGroup<NullDereference>;
 def note_indirection_through_null : Note<
   "consider using __builtin_trap() or qualifying pointer with 'volatile'">;
+def warn_pointer_indirection_from_incompatible_type : Warning<
+  "dereference of type %1 that was reinterpret_cast from type %0 has undefined "
+  "behavior.">,
+  InGroup<DiagGroup<"undefined-reinterpret-cast">>, DefaultIgnore;
 
 def err_assignment_requires_nonfragile_object : Error<
   "cannot assign to class object in non-fragile ABI (%0 invalid)">;
@@ -2904,6 +2908,9 @@ def err_bad_cxx_cast_member_pointer_size : Error<
 def err_bad_static_cast_incomplete : Error<"%0 is an incomplete type">;
 def err_bad_reinterpret_cast_reference : Error<
   "reinterpret_cast of a %0 to %1 needs its address which is not allowed">;
+def warn_undefined_reinterpret_cast : Warning<
+  "reinterpret_cast from %0 to %1 has undefined behavior.">,
+  InGroup<DiagGroup<"undefined-reinterpret-cast">>, DefaultIgnore;
 
 // These messages don't adhere to the pattern.
 // FIXME: Display the path somehow better.
index 07a14d2dca3b271e2110ff477e78eb7797c35327..1129adda49da9f0068a69994c7cc0997963a9160 100644 (file)
@@ -2587,6 +2587,10 @@ public:
                                ParsedType ObjectType,
                                bool EnteringContext);
 
+  // Checks that reinterpret casts don't have undefined behavior.
+  void CheckCompatibleReinterpretCast(QualType SrcType, QualType DestType,
+                                      bool IsDereference, SourceRange Range);
+
   /// ActOnCXXNamedCast - Parse {dynamic,static,reinterpret,const}_cast's.
   ExprResult ActOnCXXNamedCast(SourceLocation OpLoc,
                                tok::TokenKind Kind,
index ed54f0f5442528a3147f478253cc04534fa90834..2ea3b46829fd9d82e3c742ff104b8b81d6f3c170 100644 (file)
@@ -1288,6 +1288,61 @@ static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType,
   return TC_Success;
 }
 
+// Checks for undefined behavior in reinterpret_cast.
+// The cases that is checked for is:
+// *reinterpret_cast<T*>(&a)
+// reinterpret_cast<T&>(a)
+// where accessing 'a' as type 'T' will result in undefined behavior.
+void Sema::CheckCompatibleReinterpretCast(QualType SrcType, QualType DestType,
+                                          bool IsDereference,
+                                          SourceRange Range) {
+  unsigned DiagID = IsDereference ?
+                        diag::warn_pointer_indirection_from_incompatible_type :
+                        diag::warn_undefined_reinterpret_cast;
+
+  if (Diags.getDiagnosticLevel(DiagID, Range.getBegin()) ==
+          Diagnostic::Ignored) {
+    return;
+  }
+
+  QualType SrcTy, DestTy;
+  if (IsDereference) {
+    if (!SrcType->getAs<PointerType>() || !DestType->getAs<PointerType>()) {
+      return;
+    }
+    SrcTy = SrcType->getPointeeType();
+    DestTy = DestType->getPointeeType();
+  } else {
+    if (!DestType->getAs<ReferenceType>()) {
+      return;
+    }
+    SrcTy = SrcType;
+    DestTy = DestType->getPointeeType();
+  }
+
+  // Cast is compatible if the types are the same.
+  if (Context.hasSameUnqualifiedType(DestTy, SrcTy)) {
+    return;
+  }
+  // or one of the types is a char or void type
+  if (DestTy->isAnyCharacterType() || DestTy->isVoidType() ||
+      SrcTy->isAnyCharacterType() || SrcTy->isVoidType()) {
+    return;
+  }
+  // or one of the types is a tag type.
+  if (isa<TagType>(SrcTy) || isa<TagType>(DestTy)) {
+    return;
+  }
+
+  if ((SrcTy->isUnsignedIntegerType() && DestTy->isSignedIntegerType()) ||
+      (SrcTy->isSignedIntegerType() && DestTy->isUnsignedIntegerType())) {
+    if (Context.getTypeSize(DestTy) == Context.getTypeSize(SrcTy)) {
+      return;
+    }
+  }
+
+  Diag(Range.getBegin(), DiagID) << SrcType << DestType << Range;
+}
 
 static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr,
                                         QualType DestType, bool CStyle,
@@ -1324,6 +1379,11 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr,
       return TC_NotApplicable;
     }
 
+    if (!CStyle) {
+      Self.CheckCompatibleReinterpretCast(SrcType, DestType,
+                                          /*isDereference=*/false, OpRange);
+    }
+
     // C++ 5.2.10p10: [...] a reference cast reinterpret_cast<T&>(x) has the
     //   same effect as the conversion *reinterpret_cast<T*>(&x) with the
     //   built-in & and * operators.
index 20b92b84203f5d3f3fc51e5b7284f0e1820e14f5..afcde44814ca51f0556318a51d260d69e86f0b60 100644 (file)
@@ -8404,7 +8404,13 @@ static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK,
   Op = ConvResult.take();
   QualType OpTy = Op->getType();
   QualType Result;
-  
+
+  if (isa<CXXReinterpretCastExpr>(Op)) {
+    QualType OpOrigType = Op->IgnoreParenCasts()->getType();
+    S.CheckCompatibleReinterpretCast(OpOrigType, OpTy, /*IsDereference*/true,
+                                     Op->getSourceRange());
+  }
+
   // Note that per both C89 and C99, indirection is always legal, even if OpTy
   // is an incomplete type or void.  It would be possible to warn about
   // dereferencing a void pointer, but it's completely well-defined, and such a
index f5262496c4b05505e1eea699163531be93ffc268..449fecb1607b4888468286a0e7f9b5176b0ae6c6 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -ffreestanding %s
+// RUN: %clang_cc1 -fsyntax-only -verify -ffreestanding -Wundefined-reinterpret-cast %s
 
 #include <stdint.h>
 
@@ -116,3 +116,155 @@ namespace PR9564 {
   __attribute((ext_vector_type(4))) typedef float v4;
   float& w(v4 &a) { return reinterpret_cast<float&>(a[1]); } // expected-error {{not allowed}}
 }
+
+void dereference_reinterpret_cast() {
+  struct A {};
+  class B {};
+  A a;
+  B b;
+  long l;
+  double d;
+  float f;
+  char c;
+  unsigned char uc;
+  void* v_ptr;
+  (void)reinterpret_cast<double&>(l);  // expected-warning {{reinterpret_cast from 'long' to 'double &' has undefined behavior}}
+  (void)*reinterpret_cast<double*>(&l);  // expected-warning {{dereference of type 'double *' that was reinterpret_cast from type 'long *' has undefined behavior}}
+  (void)reinterpret_cast<double&>(f);  // expected-warning {{reinterpret_cast from 'float' to 'double &' has undefined behavior}}
+  (void)*reinterpret_cast<double*>(&f);  // expected-warning {{dereference of type 'double *' that was reinterpret_cast from type 'float *' has undefined behavior}}
+  (void)reinterpret_cast<float&>(l);  // expected-warning {{reinterpret_cast from 'long' to 'float &' has undefined behavior}}
+  (void)*reinterpret_cast<float*>(&l);  // expected-warning {{dereference of type 'float *' that was reinterpret_cast from type 'long *' has undefined behavior}}
+  (void)reinterpret_cast<float&>(d);  // expected-warning {{reinterpret_cast from 'double' to 'float &' has undefined behavior}}
+  (void)*reinterpret_cast<float*>(&d);  // expected-warning {{dereference of type 'float *' that was reinterpret_cast from type 'double *' has undefined behavior}}
+
+  // TODO: add warning for tag types
+  (void)reinterpret_cast<A&>(b);
+  (void)*reinterpret_cast<A*>(&b);
+  (void)reinterpret_cast<B&>(a);
+  (void)*reinterpret_cast<B*>(&a);
+
+  // Casting to itself is allowed
+  (void)reinterpret_cast<A&>(a);
+  (void)*reinterpret_cast<A*>(&a);
+  (void)reinterpret_cast<B&>(b);
+  (void)*reinterpret_cast<B*>(&b);
+  (void)reinterpret_cast<long&>(l);
+  (void)*reinterpret_cast<long*>(&l);
+  (void)reinterpret_cast<double&>(d);
+  (void)*reinterpret_cast<double*>(&d);
+  (void)reinterpret_cast<char&>(c);
+  (void)*reinterpret_cast<char*>(&c);
+
+  // Casting to and from chars are allowable
+  (void)reinterpret_cast<A&>(c);
+  (void)*reinterpret_cast<A*>(&c);
+  (void)reinterpret_cast<B&>(c);
+  (void)*reinterpret_cast<B*>(&c);
+  (void)reinterpret_cast<long&>(c);
+  (void)*reinterpret_cast<long*>(&c);
+  (void)reinterpret_cast<double&>(c);
+  (void)*reinterpret_cast<double*>(&c);
+  (void)reinterpret_cast<char&>(l);
+  (void)*reinterpret_cast<char*>(&l);
+  (void)reinterpret_cast<char&>(d);
+  (void)*reinterpret_cast<char*>(&d);
+  (void)reinterpret_cast<char&>(f);
+  (void)*reinterpret_cast<char*>(&f);
+
+  // Casting from void pointer.
+  (void)*reinterpret_cast<A*>(v_ptr);
+  (void)*reinterpret_cast<B*>(v_ptr);
+  (void)*reinterpret_cast<long*>(v_ptr);
+  (void)*reinterpret_cast<double*>(v_ptr);
+  (void)*reinterpret_cast<float*>(v_ptr);
+
+  // Casting to void pointer
+  (void)*reinterpret_cast<void*>(&a);
+  (void)*reinterpret_cast<void*>(&b);
+  (void)*reinterpret_cast<void*>(&l);
+  (void)*reinterpret_cast<void*>(&d);
+  (void)*reinterpret_cast<void*>(&f);
+}
+
+void reinterpret_cast_whitelist () {
+  // the dynamic type of the object
+  int a;
+  float b;
+  (void)reinterpret_cast<int&>(a);
+  (void)*reinterpret_cast<int*>(&a);
+  (void)reinterpret_cast<float&>(b);
+  (void)*reinterpret_cast<float*>(&b);
+
+  // a cv-qualified version of the dynamic object
+  (void)reinterpret_cast<const int&>(a);
+  (void)*reinterpret_cast<const int*>(&a);
+  (void)reinterpret_cast<volatile int&>(a);
+  (void)*reinterpret_cast<volatile int*>(&a);
+  (void)reinterpret_cast<const volatile int&>(a);
+  (void)*reinterpret_cast<const volatile int*>(&a);
+  (void)reinterpret_cast<const float&>(b);
+  (void)*reinterpret_cast<const float*>(&b);
+  (void)reinterpret_cast<volatile float&>(b);
+  (void)*reinterpret_cast<volatile float*>(&b);
+  (void)reinterpret_cast<const volatile float&>(b);
+  (void)*reinterpret_cast<const volatile float*>(&b);
+
+  // a type that is the signed or unsigned type corresponding to the dynamic
+  // type of the object
+  signed d;
+  unsigned e;
+  (void)reinterpret_cast<signed&>(d);
+  (void)*reinterpret_cast<signed*>(&d);
+  (void)reinterpret_cast<signed&>(e);
+  (void)*reinterpret_cast<signed*>(&e);
+  (void)reinterpret_cast<unsigned&>(d);
+  (void)*reinterpret_cast<unsigned*>(&d);
+  (void)reinterpret_cast<unsigned&>(e);
+  (void)*reinterpret_cast<unsigned*>(&e);
+
+  // a type that is the signed or unsigned type corresponding a cv-qualified
+  // version of the dynamic type the object
+  (void)reinterpret_cast<const signed&>(d);
+  (void)*reinterpret_cast<const signed*>(&d);
+  (void)reinterpret_cast<const signed&>(e);
+  (void)*reinterpret_cast<const signed*>(&e);
+  (void)reinterpret_cast<const unsigned&>(d);
+  (void)*reinterpret_cast<const unsigned*>(&d);
+  (void)reinterpret_cast<const unsigned&>(e);
+  (void)*reinterpret_cast<const unsigned*>(&e);
+  (void)reinterpret_cast<volatile signed&>(d);
+  (void)*reinterpret_cast<volatile signed*>(&d);
+  (void)reinterpret_cast<volatile signed&>(e);
+  (void)*reinterpret_cast<volatile signed*>(&e);
+  (void)reinterpret_cast<volatile unsigned&>(d);
+  (void)*reinterpret_cast<volatile unsigned*>(&d);
+  (void)reinterpret_cast<volatile unsigned&>(e);
+  (void)*reinterpret_cast<volatile unsigned*>(&e);
+  (void)reinterpret_cast<const volatile signed&>(d);
+  (void)*reinterpret_cast<const volatile signed*>(&d);
+  (void)reinterpret_cast<const volatile signed&>(e);
+  (void)*reinterpret_cast<const volatile signed*>(&e);
+  (void)reinterpret_cast<const volatile unsigned&>(d);
+  (void)*reinterpret_cast<const volatile unsigned*>(&d);
+  (void)reinterpret_cast<const volatile unsigned&>(e);
+  (void)*reinterpret_cast<const volatile unsigned*>(&e);
+
+  // an aggregate or union type that includes one of the aforementioned types
+  // among its members (including, recursively, a member of a subaggregate or
+  // contained union)
+  // TODO: checking is not implemented for tag types
+
+  // a type that is a (possible cv-qualified) base class type of the dynamic
+  // type of the object
+  // TODO: checking is not implemented for tag types
+
+  // a char or unsigned char type
+  (void)reinterpret_cast<char&>(a);
+  (void)*reinterpret_cast<char*>(&a);
+  (void)reinterpret_cast<unsigned char&>(a);
+  (void)*reinterpret_cast<unsigned char*>(&a);
+  (void)reinterpret_cast<char&>(b);
+  (void)*reinterpret_cast<char*>(&b);
+  (void)reinterpret_cast<unsigned char&>(b);
+  (void)*reinterpret_cast<unsigned char*>(&b);
+}