]> granicus.if.org Git - clang/commitdiff
Fix a horrible bug in our handling of C-style casting, where a C-style
authorDouglas Gregor <dgregor@apple.com>
Thu, 27 Jan 2011 00:58:17 +0000 (00:58 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 27 Jan 2011 00:58:17 +0000 (00:58 +0000)
derived-to-base cast that also casts away constness (one of the cases
for static_cast followed by const_cast) would be treated as a bit-cast
rather than a derived-to-base class, causing miscompiles and
heartburn.

Fixes <rdar://problem/8913298>.

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

include/clang/Sema/Sema.h
lib/Sema/SemaCXXCast.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaInit.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaTemplate.cpp
test/CXX/expr/expr.cast/p4.cpp [new file with mode: 0644]

index 7d51fc34bc66c980d9cc0acfdb74637ed9d15e42..5db08ba83017b506e134c57954b65cb48c877633 100644 (file)
@@ -999,7 +999,8 @@ public:
                              Expr *From,
                              bool SuppressUserConversions,
                              bool AllowExplicit,
-                             bool InOverloadResolution);
+                             bool InOverloadResolution,
+                             bool CStyle);
 
   bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType);
   bool IsFloatingPointPromotion(QualType FromType, QualType ToType);
@@ -1023,7 +1024,8 @@ public:
                                     CastKind &Kind,
                                     CXXCastPath &BasePath,
                                     bool IgnoreBaseAccess);
-  bool IsQualificationConversion(QualType FromType, QualType ToType);
+  bool IsQualificationConversion(QualType FromType, QualType ToType,
+                                 bool CStyle);
   bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType);
 
 
@@ -4559,10 +4561,11 @@ public:
   bool PerformImplicitConversion(Expr *&From, QualType ToType,
                                  const ImplicitConversionSequence& ICS,
                                  AssignmentAction Action,
-                                 bool IgnoreBaseAccess = false);
+                                 bool CStyle = false);
   bool PerformImplicitConversion(Expr *&From, QualType ToType,
                                  const StandardConversionSequence& SCS,
-                                 AssignmentAction Action,bool IgnoreBaseAccess);
+                                 AssignmentAction Action,
+                                 bool CStyle);
 
   /// the following "Check" methods will return a valid/converted QualType
   /// or a null QualType (indicating an error diagnostic was issued).
index c492c2ac7dec573732744d662b73e3dfffa1d7db..afbfa17e776133a51aecbe226a6cc20c38281c6f 100644 (file)
@@ -320,7 +320,7 @@ CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType) {
 
   // Test if they're compatible.
   return SrcConstruct != DestConstruct &&
-    !Self.IsQualificationConversion(SrcConstruct, DestConstruct);
+    !Self.IsQualificationConversion(SrcConstruct, DestConstruct, false);
 }
 
 /// CheckDynamicCast - Check that a dynamic_cast\<DestType\>(SrcExpr) is valid.
@@ -1035,8 +1035,7 @@ TryStaticImplicitCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
   
   InitializedEntity Entity = InitializedEntity::InitializeTemporary(DestType);
   InitializationKind InitKind
-    = InitializationKind::CreateCast(/*FIXME:*/OpRange, 
-                                                               CStyle);    
+    = InitializationKind::CreateCast(/*FIXME:*/OpRange, CStyle);    
   InitializationSequence InitSeq(Self, Entity, InitKind, &SrcExpr, 1);
 
   // At this point of CheckStaticCast, if the destination is a reference,
index 062d4f697ab4da320c25f01543b94b422fdc6627..acdc15fdf2836fbef96bf4c923d76db899021f41 100644 (file)
@@ -1737,11 +1737,11 @@ static ExprResult BuildCXXCastArgument(Sema &S,
 bool
 Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
                                 const ImplicitConversionSequence &ICS,
-                                AssignmentAction Action, bool IgnoreBaseAccess) {
+                                AssignmentAction Action, bool CStyle) {
   switch (ICS.getKind()) {
   case ImplicitConversionSequence::StandardConversion:
     if (PerformImplicitConversion(From, ToType, ICS.Standard, Action,
-                                  IgnoreBaseAccess))
+                                  CStyle))
       return true;
     break;
 
@@ -1772,7 +1772,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
       if (!ICS.UserDefined.EllipsisConversion) {
         if (PerformImplicitConversion(From, BeforeToType, 
                                       ICS.UserDefined.Before, AA_Converting,
-                                      IgnoreBaseAccess))
+                                      CStyle))
           return true;
       }
     
@@ -1790,7 +1790,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
       From = CastArg.takeAs<Expr>();
 
       return PerformImplicitConversion(From, ToType, ICS.UserDefined.After,
-                                       AA_Converting, IgnoreBaseAccess);
+                                       AA_Converting, CStyle);
   }
 
   case ImplicitConversionSequence::AmbiguousConversion:
@@ -1820,7 +1820,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
 bool
 Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
                                 const StandardConversionSequence& SCS,
-                                AssignmentAction Action, bool IgnoreBaseAccess) {
+                                AssignmentAction Action, bool CStyle) {
   // Overall FIXME: we are recomputing too many types here and doing far too
   // much extra work. What this means is that we need to keep track of more
   // information that is computed when we try the implicit conversion initially,
@@ -1982,7 +1982,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
     
     CastKind Kind = CK_Invalid;
     CXXCastPath BasePath;
-    if (CheckPointerConversion(From, ToType, Kind, BasePath, IgnoreBaseAccess))
+    if (CheckPointerConversion(From, ToType, Kind, BasePath, CStyle))
       return true;
     ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath);
     break;
@@ -1991,8 +1991,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
   case ICK_Pointer_Member: {
     CastKind Kind = CK_Invalid;
     CXXCastPath BasePath;
-    if (CheckMemberPointerConversion(From, ToType, Kind, BasePath,
-                                     IgnoreBaseAccess))
+    if (CheckMemberPointerConversion(From, ToType, Kind, BasePath, CStyle))
       return true;
     if (CheckExceptionSpecCompatibility(From, ToType))
       return true;
@@ -2022,7 +2021,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
                                      From->getLocStart(),
                                      From->getSourceRange(), 
                                      &BasePath,
-                                     IgnoreBaseAccess))
+                                     CStyle))
       return true;
 
     ImpCastExprToType(From, ToType.getNonReferenceType(),
index 00a13b54cf7164eeb1fb8c6772ae077bbca0736c..b34fe2ff284c04976e49155a2c2928e055f48f88 100644 (file)
@@ -2519,7 +2519,9 @@ static void TryReferenceInitialization(Sema &S,
   bool T1Function = T1->isFunctionType();
   if (isLValueRef || T1Function) {
     if (InitCategory.isLValue() && 
-        RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification) {
+        (RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification ||
+         (Kind.isCStyleOrFunctionalCast() && 
+          RefRelationship == Sema::Ref_Related))) {
       //   - is an lvalue (but is not a bit-field), and "cv1 T1" is 
       //     reference-compatible with "cv2 T2," or
       //
@@ -2593,7 +2595,9 @@ static void TryReferenceInitialization(Sema &S,
   //        "cv1 T1" is reference-compatible with "cv2 T2"
   // Note: functions are handled below.
   if (!T1Function &&
-      RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification &&
+      (RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification ||
+       (Kind.isCStyleOrFunctionalCast() && 
+        RefRelationship == Sema::Ref_Related)) &&
       (InitCategory.isXValue() ||
        (InitCategory.isPRValue() && T2->isRecordType()) ||
        (InitCategory.isPRValue() && T2->isArrayType()))) {
@@ -2630,9 +2634,6 @@ static void TryReferenceInitialization(Sema &S,
   //         reference-related to T2, and can be implicitly converted to an 
   //         xvalue, class prvalue, or function lvalue of type "cv3 T3",
   //         where "cv1 T1" is reference-compatible with "cv3 T3",
-  //
-  // FIXME: Need to handle xvalue, class prvalue, etc. cases in 
-  // TryRefInitWithConversionFunction.
   if (T2->isRecordType()) {
     if (RefRelationship == Sema::Ref_Incompatible) {
       ConvOvlResult = TryRefInitWithConversionFunction(S, Entity,
@@ -2665,7 +2666,8 @@ static void TryReferenceInitialization(Sema &S,
   if (S.TryImplicitConversion(Sequence, TempEntity, Initializer,
                               /*SuppressUserConversions*/ false,
                               AllowExplicit,
-                              /*FIXME:InOverloadResolution=*/false)) {
+                              /*FIXME:InOverloadResolution=*/false,
+                              /*CStyle=*/Kind.isCStyleOrFunctionalCast())) {
     // FIXME: Use the conversion function set stored in ICS to turn
     // this into an overloading ambiguity diagnostic. However, we need
     // to keep that set as an OverloadCandidateSet rather than as some
@@ -3184,7 +3186,8 @@ InitializationSequence::InitializationSequence(Sema &S,
   if (S.TryImplicitConversion(*this, Entity, Initializer,
                               /*SuppressUserConversions*/ true,
                               /*AllowExplicitConversions*/ false,
-                              /*InOverloadResolution*/ false))
+                              /*InOverloadResolution*/ false,
+                              /*CStyle=*/Kind.isCStyleOrFunctionalCast()))
   {
     if (Initializer->getType() == Context.OverloadTy)
       SetFailed(InitializationSequence::FK_AddressOfOverloadFailed);
@@ -3844,11 +3847,9 @@ InitializationSequence::Perform(Sema &S,
     }
 
     case SK_ConversionSequence: {
-      bool IgnoreBaseAccess = Kind.isCStyleOrFunctionalCast();
-
       if (S.PerformImplicitConversion(CurInitExpr, Step->Type, *Step->ICS,
                                       getAssignmentAction(Entity),
-                                      IgnoreBaseAccess))
+                                      Kind.isCStyleOrFunctionalCast()))
         return ExprError();
         
       CurInit.release();
index 0b01332700ca9f0a249145b3cde670db777dd8a6..f33f42d85649fa5b388803d9508baef9f06a86de 100644 (file)
@@ -46,7 +46,8 @@ CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn,
 
 static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
                                  bool InOverloadResolution,
-                                 StandardConversionSequence &SCS);
+                                 StandardConversionSequence &SCS,
+                                 bool CStyle);
 static OverloadingResult
 IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
                         UserDefinedConversionSequence& User,
@@ -744,10 +745,11 @@ static ImplicitConversionSequence
 TryImplicitConversion(Sema &S, Expr *From, QualType ToType,
                       bool SuppressUserConversions,
                       bool AllowExplicit, 
-                      bool InOverloadResolution) {
+                      bool InOverloadResolution,
+                      bool CStyle) {
   ImplicitConversionSequence ICS;
   if (IsStandardConversion(S, From, ToType, InOverloadResolution,
-                           ICS.Standard)) {
+                           ICS.Standard, CStyle)) {
     ICS.setStandard();
     return ICS;
   }
@@ -858,12 +860,14 @@ bool Sema::TryImplicitConversion(InitializationSequence &Sequence,
                                  Expr *Initializer,
                                  bool SuppressUserConversions,
                                  bool AllowExplicitConversions,
-                                 bool InOverloadResolution) {
+                                 bool InOverloadResolution,
+                                 bool CStyle) {
   ImplicitConversionSequence ICS
     = clang::TryImplicitConversion(*this, Initializer, Entity.getType(),
                                    SuppressUserConversions,
                                    AllowExplicitConversions, 
-                                   InOverloadResolution);
+                                   InOverloadResolution,
+                                   CStyle);
   if (ICS.isBad()) return true;
 
   // Perform the actual conversion.
@@ -891,7 +895,8 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
   ICS = clang::TryImplicitConversion(*this, From, ToType,
                                      /*SuppressUserConversions=*/false,
                                      AllowExplicit,
-                                     /*InOverloadResolution=*/false);
+                                     /*InOverloadResolution=*/false,
+                                     /*CStyle=*/false);
   return PerformImplicitConversion(From, ToType, ICS, Action);
 }
   
@@ -999,7 +1004,8 @@ static bool IsVectorConversion(ASTContext &Context, QualType FromType,
 /// routine will return false and the value of SCS is unspecified.
 static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
                                  bool InOverloadResolution,
-                                 StandardConversionSequence &SCS) {
+                                 StandardConversionSequence &SCS,
+                                 bool CStyle) {
   QualType FromType = From->getType();
   
   // Standard conversions (C++ [conv])
@@ -1189,7 +1195,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
   QualType CanonFrom;
   QualType CanonTo;
   // The third conversion can be a qualification conversion (C++ 4p1).
-  if (S.IsQualificationConversion(FromType, ToType)) {
+  if (S.IsQualificationConversion(FromType, ToType, CStyle)) {
     SCS.Third = ICK_Qualification;
     FromType = ToType;
     CanonFrom = S.Context.getCanonicalType(FromType);
@@ -1984,7 +1990,8 @@ bool Sema::CheckMemberPointerConversion(Expr *From, QualType ToType,
 /// an rvalue of type FromType to ToType is a qualification conversion
 /// (C++ 4.4).
 bool
-Sema::IsQualificationConversion(QualType FromType, QualType ToType) {
+Sema::IsQualificationConversion(QualType FromType, QualType ToType, 
+                                bool CStyle) {
   FromType = Context.getCanonicalType(FromType);
   ToType = Context.getCanonicalType(ToType);
 
@@ -2009,12 +2016,12 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType) {
 
     //   -- for every j > 0, if const is in cv 1,j then const is in cv
     //      2,j, and similarly for volatile.
-    if (!ToType.isAtLeastAsQualifiedAs(FromType))
+    if (!CStyle && !ToType.isAtLeastAsQualifiedAs(FromType))
       return false;
 
     //   -- if the cv 1,j and cv 2,j are different, then const is in
     //      every cv for 0 < k < j.
-    if (FromType.getCVRQualifiers() != ToType.getCVRQualifiers()
+    if (!CStyle && FromType.getCVRQualifiers() != ToType.getCVRQualifiers()
         && !PreviousToQualsIncludeConst)
       return false;
 
@@ -3156,7 +3163,8 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType,
   //   and does not constitute a conversion.
   ICS = TryImplicitConversion(S, Init, T1, SuppressUserConversions,
                               /*AllowExplicit=*/false,
-                              /*InOverloadResolution=*/false);
+                              /*InOverloadResolution=*/false,
+                              /*CStyle=*/false);
 
   // Of course, that's still a reference binding.
   if (ICS.isStandard()) {
@@ -3195,7 +3203,8 @@ TryCopyInitialization(Sema &S, Expr *From, QualType ToType,
   return TryImplicitConversion(S, From, ToType,
                                SuppressUserConversions,
                                /*AllowExplicit=*/false,
-                               InOverloadResolution);
+                               InOverloadResolution,
+                               /*CStyle=*/false);
 }
 
 /// TryObjectArgumentInitialization - Try to initialize the object
@@ -3379,7 +3388,8 @@ TryContextuallyConvertToBool(Sema &S, Expr *From) {
                                // FIXME: Are these flags correct?
                                /*SuppressUserConversions=*/false,
                                /*AllowExplicit=*/true,
-                               /*InOverloadResolution=*/false);
+                               /*InOverloadResolution=*/false,
+                               /*CStyle=*/false);
 }
 
 /// PerformContextuallyConvertToBool - Perform a contextual conversion
@@ -3405,7 +3415,8 @@ TryContextuallyConvertToObjCId(Sema &S, Expr *From) {
                                // FIXME: Are these flags correct?
                                /*SuppressUserConversions=*/false,
                                /*AllowExplicit=*/true,
-                               /*InOverloadResolution=*/false);
+                               /*InOverloadResolution=*/false,
+                               /*CStyle=*/false);
 }
 
 /// PerformContextuallyConvertToObjCId - Perform a contextual conversion
index 78f8d5e05af7bcd93a74d512d2be7f2b785e7a74..eed41cc396c4630bbdc1ac627b26d37b80c148af 100644 (file)
@@ -3085,7 +3085,7 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S,
 
   if (ParamType->isPointerType() && 
       !ParamType->getAs<PointerType>()->getPointeeType()->isFunctionType() &&
-      S.IsQualificationConversion(ArgType, ParamType)) {
+      S.IsQualificationConversion(ArgType, ParamType, false)) {
     // For pointer-to-object types, qualification conversions are
     // permitted.
   } else {
@@ -3429,7 +3429,8 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
                                                             ParamType, 
                                                             Arg, Converted);
 
-    if (IsQualificationConversion(ArgType, ParamType.getNonReferenceType())) {
+    if (IsQualificationConversion(ArgType, ParamType.getNonReferenceType(),
+                                  false)) {
       ImpCastExprToType(Arg, ParamType, CK_NoOp, CastCategory(Arg));
     } else if (!Context.hasSameUnqualifiedType(ArgType,
                                            ParamType.getNonReferenceType())) {
@@ -3492,7 +3493,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
 
   if (Context.hasSameUnqualifiedType(ParamType, ArgType)) {
     // Types match exactly: nothing more to do here.
-  } else if (IsQualificationConversion(ArgType, ParamType)) {
+  } else if (IsQualificationConversion(ArgType, ParamType, false)) {
     ImpCastExprToType(Arg, ParamType, CK_NoOp, CastCategory(Arg));
   } else {
     // We can't perform this conversion.
@@ -3597,7 +3598,7 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg,
       // the element type on the parameter could be more qualified than the
       // element type in the expression we constructed.
       if (IsQualificationConversion(((Expr*) RefExpr.get())->getType(),
-                                    ParamType.getUnqualifiedType())) {
+                                    ParamType.getUnqualifiedType(), false)) {
         Expr *RefE = RefExpr.takeAs<Expr>();
         ImpCastExprToType(RefE, ParamType.getUnqualifiedType(), CK_NoOp);
         RefExpr = Owned(RefE);
diff --git a/test/CXX/expr/expr.cast/p4.cpp b/test/CXX/expr/expr.cast/p4.cpp
new file mode 100644 (file)
index 0000000..d3f9785
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -ast-dump %s | FileCheck %s
+
+struct A { int x; };
+struct B { int y; };
+struct C : A, B { };
+
+// CHECK: casting_away_constness
+void casting_away_constness(const B &b, const C &c, const B *bp, const C *cp) {
+  // CHECK: DerivedToBase (B)
+  // CHECK: DeclRefExpr {{.*}} ParmVar='c'
+  (void)(B&)c;
+  // CHECK: BaseToDerived (B)
+  // CHECK: DeclRefExpr {{.*}} ParmVar='b'
+  (void)(C&)b;
+  // CHECK: DerivedToBase (B)
+  // CHECK: DeclRefExpr {{.*}} ParmVar='cp'
+  (void)(B*)cp;
+  // CHECK: BaseToDerived (B)
+  // CHECK: DeclRefExpr {{.*}} ParmVar='bp'
+  (void)(C*)bp;
+  // CHECK: ReturnStmt
+  return;
+}