]> granicus.if.org Git - clang/commitdiff
Implement the core checking for compatible exception specifications in assignment...
authorSebastian Redl <sebastian.redl@getdesigned.at>
Sat, 10 Oct 2009 12:04:10 +0000 (12:04 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Sat, 10 Oct 2009 12:04:10 +0000 (12:04 +0000)
The exception specification of the assignee must be the same or a subset of the target. In addition, exception specifications on arguments and return types must be equivalent, but this is not implemented yet.
This currently produces two diagnostics for every invalid assignment/initialization, due to the diagnostic produced outside PerformImplicitConversion, e.g. in CheckSingleInitializer. I don't know how to suppress this; in any case I think it is the wrong place for a diagnostic, since there are other diagnostics produced inside the function. So I'm leaving it as it is for the moment.

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

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaType.cpp
test/SemaCXX/exception-spec.cpp

index 3b4af8e9b5e19c23ac5bad58729dac0bc891affa..f3d69c0035ae9823ea15f4d8cc931929d5d86e1f 100644 (file)
@@ -363,6 +363,8 @@ def err_mismatched_exception_spec : Error<
 def err_override_exception_spec : Error<
   "exception specification of overriding function is more lax than "
   "base version">;
+def err_incompatible_exception_specs : Error<
+  "target exception specification is not superset of source">;
 
 // C++ access checking
 def err_class_redeclared_with_different_access : Error<
index 9d35f3dd3f57b58f19ad0f2d3f1177ce2e43a4f5..56eb24edcfefe55cb4944a718ae7e7f042890071 100644 (file)
@@ -3519,6 +3519,8 @@ public:
 
   bool IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType);
 
+  bool CheckExceptionSpecCompatibility(Expr *From, QualType ToType);
+
   bool PerformImplicitConversion(Expr *&From, QualType ToType,
                                  const char *Flavor,
                                  bool AllowExplicit = false,
index fb0e1440365851709f900f869ba0ef3bca0c56dd..9a90be418994265ab145f952f002d5b2f7b838cf 100644 (file)
@@ -3630,6 +3630,8 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType,
       CastExpr::CastKind CK = CastExpr::CK_NoOp;
       if (DerivedToBase)
         CK = CastExpr::CK_DerivedToBase;
+      else if(CheckExceptionSpecCompatibility(Init, T1))
+        return true;
       ImpCastExprToType(Init, T1, CK, /*isLvalue=*/true);
     }
   }
@@ -3703,7 +3705,9 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType,
                                cast<CXXMethodDecl>(Best->Function), 
                                Owned(Init));
         Init = InitConversion.takeAs<Expr>();
-                                    
+
+        if (CheckExceptionSpecCompatibility(Init, T1))
+          return true;
         ImpCastExprToType(Init, T1, CastExpr::CK_UserDefinedConversion, 
                           /*isLvalue=*/true);
       }
@@ -3792,6 +3796,8 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType,
       CastExpr::CastKind CK = CastExpr::CK_NoOp;
       if (DerivedToBase)
         CK = CastExpr::CK_DerivedToBase;
+      else if(CheckExceptionSpecCompatibility(Init, T1))
+        return true;
       ImpCastExprToType(Init, T1, CK, /*isLvalue=*/false);
     }
     return false;
index d7462375630342820127a515a256df5b3c313117..eb0b6e1eff5207213cd78772a05460a1f598efce 100644 (file)
@@ -1197,7 +1197,11 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
   // Perform the second implicit conversion
   switch (SCS.Second) {
   case ICK_Identity:
-    // Nothing to do.
+    // If both sides are functions (or pointers/references to them), there could
+    // be incompatible exception declarations.
+    if (CheckExceptionSpecCompatibility(From, ToType))
+      return true;
+    // Nothing else to do.
     break;
 
   case ICK_Integral_Promotion:
@@ -1235,6 +1239,8 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
     CastExpr::CastKind Kind = CastExpr::CK_Unknown;
     if (CheckMemberPointerConversion(From, ToType, Kind))
       return true;
+    if (CheckExceptionSpecCompatibility(From, ToType))
+      return true;
     ImpCastExprToType(From, ToType, Kind);
     break;
   }
@@ -1269,6 +1275,38 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
   return false;
 }
 
+bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType)
+{
+  // First we check for applicability.
+  // Target type must be a function, function pointer or function reference.
+  if (const PointerType *PtrTy = ToType->getAs<PointerType>())
+    ToType = PtrTy->getPointeeType();
+  else if (const ReferenceType *RefTy = ToType->getAs<ReferenceType>())
+    ToType = RefTy->getPointeeType();
+
+  const FunctionProtoType *ToFunc = ToType->getAs<FunctionProtoType>();
+  if (!ToFunc)
+    return false;
+
+  // SourceType must be a function or function pointer.
+  // References are treated as functions.
+  QualType FromType = From->getType();
+  if (const PointerType *PtrTy = FromType->getAs<PointerType>())
+    FromType = PtrTy->getPointeeType();
+
+  const FunctionProtoType *FromFunc = FromType->getAs<FunctionProtoType>();
+  if (!FromFunc)
+    return false;
+
+  // Now we've got the correct types on both sides, check their compatibility.
+  // This means that the source of the conversion can only throw a subset of
+  // the exceptions of the target, and any exception specs on arguments or
+  // return types must be equivalent.
+  return CheckExceptionSpecSubset(diag::err_incompatible_exception_specs,
+                                  0, ToFunc, From->getSourceRange().getBegin(),
+                                  FromFunc, SourceLocation());
+}
+
 Sema::OwningExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait OTT,
                                                  SourceLocation KWLoc,
                                                  SourceLocation LParen,
index 6a5db5f2ad385b9f76f8b653768adab8d98fc534..5c7bbd04419914ed49462d805bccc2f718533f04 100644 (file)
@@ -1512,6 +1512,9 @@ bool Sema::CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID,
   // FIXME: As usual, we could be more specific in our error messages, but
   // that better waits until we've got types with source locations.
 
+  if (!SubLoc.isValid())
+    SubLoc = SuperLoc;
+
   // If superset contains everything, we're done.
   if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec())
     return false;
@@ -1519,7 +1522,8 @@ bool Sema::CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID,
   // It does not. If the subset contains everything, we've failed.
   if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) {
     Diag(SubLoc, DiagID);
-    Diag(SuperLoc, NoteID);
+    if (NoteID != 0)
+      Diag(SuperLoc, NoteID);
     return true;
   }
 
@@ -1584,7 +1588,8 @@ bool Sema::CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID,
     }
     if (!Contained) {
       Diag(SubLoc, DiagID);
-      Diag(SuperLoc, NoteID);
+      if (NoteID != 0)
+        Diag(SuperLoc, NoteID);
       return true;
     }
   }
index 9d656ad212629098293273acdcb2d5c17211618a..cb4cbf4a709d91fbb867e950e33c510bf3e5ea75 100644 (file)
@@ -134,25 +134,25 @@ void fnptrs()
 {
   // Assignment and initialization of function pointers.
   void (*t1)() throw() = &s1;    // valid
-  t1 = &s2;                      // invalid
-  t1 = &s3;                      // invalid
-  void (&t2)() throw() = s2;     // invalid
+  t1 = &s2;                      // expected-error {{not superset}} expected-error {{incompatible type}}
+  t1 = &s3;                      // expected-error {{not superset}} expected-error {{incompatible type}}
+  void (&t2)() throw() = s2;     // expected-error {{not superset}}
   void (*t3)() throw(int) = &s2; // valid
   void (*t4)() throw(A) = &s1;   // valid
   t4 = &s3;                      // valid
   t4 = &s4;                      // valid
-  t4 = &s5;                      // invalid
+  t4 = &s5;                      // expected-error {{not superset}} expected-error {{incompatible type}}
   void (*t5)() = &s1;            // valid
   t5 = &s2;                      // valid
   t5 = &s6;                      // valid
   t5 = &s7;                      // valid
-  t1 = t3;                       // invalid
+  t1 = t3;                       // expected-error {{not superset}} expected-error {{incompatible type}}
   t3 = t1;                       // valid
   void (*t6)() throw(B1);
-  t6 = t4;                       // invalid
+  t6 = t4;                       // expected-error {{not superset}} expected-error {{incompatible type}}
   t4 = t6;                       // valid
   t5 = t1;                       // valid
-  t1 = t5;                       // invalid
+  t1 = t5;                       // expected-error {{not superset}} expected-error {{incompatible type}}
 
   // return types and arguments must match exactly, no inheritance allowed
   void (*(*t7)())() throw(B1) = &s8;       // valid