diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 7 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 4 | ||||
-rw-r--r-- | lib/Sema/SemaCXXCast.cpp | 60 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 8 | ||||
-rw-r--r-- | test/SemaCXX/reinterpret-cast.cpp | 154 |
5 files changed, 231 insertions, 2 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 0a0c91ac91..aa7f64a246 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 07a14d2dca..1129adda49 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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, diff --git a/lib/Sema/SemaCXXCast.cpp b/lib/Sema/SemaCXXCast.cpp index ed54f0f544..2ea3b46829 100644 --- a/lib/Sema/SemaCXXCast.cpp +++ b/lib/Sema/SemaCXXCast.cpp @@ -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. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 20b92b8420..afcde44814 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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 diff --git a/test/SemaCXX/reinterpret-cast.cpp b/test/SemaCXX/reinterpret-cast.cpp index f5262496c4..449fecb160 100644 --- a/test/SemaCXX/reinterpret-cast.cpp +++ b/test/SemaCXX/reinterpret-cast.cpp @@ -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); +} |