[cfe-commits] r130703 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/Sema.h lib/Sema/SemaCXXCast.cpp lib/Sema/SemaExpr.cpp test/SemaCXX/reinterpret-cast.cpp

Argyrios Kyrtzidis akyrtzi at gmail.com
Mon May 2 11:21:19 PDT 2011


Author: akirtzidis
Date: Mon May  2 13:21:19 2011
New Revision: 130703

URL: http://llvm.org/viewvc/llvm-project?rev=130703&view=rev
Log:
Add a warning for when reinterpret_cast leads to undefined behavior, patch by Richard Trieu!

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

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=130703&r1=130702&r2=130703&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May  2 13:21:19 2011
@@ -2641,6 +2641,10 @@
   "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_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.

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=130703&r1=130702&r2=130703&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon May  2 13:21:19 2011
@@ -2587,6 +2587,10 @@
                                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,

Modified: cfe/trunk/lib/Sema/SemaCXXCast.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCXXCast.cpp?rev=130703&r1=130702&r2=130703&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCXXCast.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCXXCast.cpp Mon May  2 13:21:19 2011
@@ -1288,6 +1288,61 @@
   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 @@
       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.

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=130703&r1=130702&r2=130703&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Mon May  2 13:21:19 2011
@@ -8404,7 +8404,13 @@
   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

Modified: cfe/trunk/test/SemaCXX/reinterpret-cast.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/reinterpret-cast.cpp?rev=130703&r1=130702&r2=130703&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/reinterpret-cast.cpp (original)
+++ cfe/trunk/test/SemaCXX/reinterpret-cast.cpp Mon May  2 13:21:19 2011
@@ -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 @@
   __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);
+}





More information about the cfe-commits mailing list