]> granicus.if.org Git - clang/commitdiff
Implement checking of exception spec compatibility for overriding virtual functions.
authorSebastian Redl <sebastian.redl@getdesigned.at>
Tue, 7 Jul 2009 20:29:57 +0000 (20:29 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Tue, 7 Jul 2009 20:29:57 +0000 (20:29 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@74943 91177308-0d34-0410-b5e6-96231b3b80d8

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

index 32f2d519583b301806f446f0bd1b28a54705e066..50a4ff5327169192fb017bb99a9a16716ae3d3bf 100644 (file)
@@ -289,6 +289,9 @@ def err_incomplete_in_exception_spec : Error<
   "in exception specification">;
 def err_mismatched_exception_spec : Error<
   "exception specification in declaration does not match previous declaration">;
+def err_override_exception_spec : Error<
+  "exception specification of overriding function is more lax than "
+  "base version">;
 
 // C++ access checking
 def err_class_redeclared_with_different_access : Error<
index 4c213b65947fa87bd2ea821856a511bea2e5724f..99d3566726b5169d0c339d160d0e4a46aecea87a 100644 (file)
@@ -398,6 +398,9 @@ public:
   bool CheckEquivalentExceptionSpec(
       const FunctionProtoType *Old, SourceLocation OldLoc,
       const FunctionProtoType *New, SourceLocation NewLoc);
+  bool CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID,
+      const FunctionProtoType *Superset, SourceLocation SuperLoc,
+      const FunctionProtoType *Subset, SourceLocation SubLoc);
 
   QualType ObjCGetTypeForMethodDefinition(DeclPtrTy D);
 
@@ -1981,11 +1984,15 @@ public:
   
   std::string getAmbiguousPathsDisplayString(BasePaths &Paths);
   
-  /// CheckReturnTypeCovariance - Checks whether two types are covariant, 
-  /// according to C++ [class.virtual]p5.
-  bool CheckOverridingFunctionReturnType(const CXXMethodDecl *New, 
+  /// CheckOverridingFunctionReturnType - Checks whether the return types are
+  /// covariant, according to C++ [class.virtual]p5.
+  bool CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
                                          const CXXMethodDecl *Old);
-  
+
+  /// CheckOverridingFunctionExceptionSpec - Checks whether the exception
+  /// spec is a subset of base spec.
+  bool CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
+                                            const CXXMethodDecl *Old);
 
   //===--------------------------------------------------------------------===//
   // C++ Access Control
index 85210f049d25ecf73ce36fc806e5d5cf0b7d6570..290ab5d708ffb3c2caf224f93e41aa38463b84b5 100644 (file)
@@ -2244,7 +2244,8 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
       for (BasePaths::decl_iterator I = Paths.found_decls_begin(), 
            E = Paths.found_decls_end(); I != E; ++I) {
         if (CXXMethodDecl *OldMD = dyn_cast<CXXMethodDecl>(*I)) {
-          if (!CheckOverridingFunctionReturnType(NewMD, OldMD))
+          if (!CheckOverridingFunctionReturnType(NewMD, OldMD) &&
+              !CheckOverridingFunctionExceptionSpec(NewMD, OldMD))
             NewMD->addOverriddenMethod(OldMD);
         }
       }
index 75ceb1916555037b953cd66f7962e2c8a2ca6fd8..72adcc22d10c649d7d30398d512e394e49e00584 100644 (file)
@@ -882,7 +882,7 @@ namespace {
          i != e; ++i) {
       // Traverse the record, looking for methods.
       if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(*i)) {
-        // If the method is pre virtual, add it to the methods vector.
+        // If the method is pure virtual, add it to the methods vector.
         if (MD->isPure()) {
           Methods.push_back(MD);
           continue;
@@ -3223,6 +3223,17 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
   return false;
 }
 
+bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
+                                                const CXXMethodDecl *Old)
+{
+  return CheckExceptionSpecSubset(diag::err_override_exception_spec,
+                                  diag::note_overridden_virtual_function,
+                                  Old->getType()->getAsFunctionProtoType(),
+                                  Old->getLocation(),
+                                  New->getType()->getAsFunctionProtoType(),
+                                  New->getLocation());
+}
+
 /// ActOnCXXEnterDeclInitializer - Invoked when we are about to parse an
 /// initializer for the declaration 'Dcl'.
 /// After this method is called, according to [C++ 3.4.1p13], if 'Dcl' is a
index da61e6eb9eb650396b4c7549cfeec6c8c4c962eb..8dcf4e48f5b45ff0adf3f1d1282d0d1554ab281d 100644 (file)
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Sema.h"
+#include "SemaInherit.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
@@ -1237,6 +1238,95 @@ bool Sema::CheckEquivalentExceptionSpec(
   return true;
 }
 
+/// CheckExceptionSpecSubset - Check whether the second function type's
+/// exception specification is a subset (or equivalent) of the first function
+/// type. This is used by override and pointer assignment checks.
+bool Sema::CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID,
+    const FunctionProtoType *Superset, SourceLocation SuperLoc,
+    const FunctionProtoType *Subset, SourceLocation SubLoc)
+{
+  // 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 superset contains everything, we're done.
+  if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec())
+    return false;
+
+  // It does not. If the subset contains everything, we've failed.
+  if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) {
+    Diag(SubLoc, DiagID);
+    Diag(SuperLoc, NoteID);
+    return true;
+  }
+
+  // Neither contains everything. Do a proper comparison.
+  for (FunctionProtoType::exception_iterator SubI = Subset->exception_begin(),
+       SubE = Subset->exception_end(); SubI != SubE; ++SubI) {
+    // Take one type from the subset.
+    QualType CanonicalSubT = Context.getCanonicalType(*SubI);
+    bool SubIsPointer = false;
+    if (const ReferenceType *RefTy = CanonicalSubT->getAsReferenceType())
+      CanonicalSubT = RefTy->getPointeeType();
+    if (const PointerType *PtrTy = CanonicalSubT->getAsPointerType()) {
+      CanonicalSubT = PtrTy->getPointeeType();
+      SubIsPointer = true;
+    }
+    bool SubIsClass = CanonicalSubT->isRecordType();
+    CanonicalSubT.setCVRQualifiers(0);
+
+    BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
+                    /*DetectVirtual=*/false);
+
+    bool Contained = false;
+    // Make sure it's in the superset.
+    for (FunctionProtoType::exception_iterator SuperI =
+           Superset->exception_begin(), SuperE = Superset->exception_end();
+         SuperI != SuperE; ++SuperI) {
+      QualType CanonicalSuperT = Context.getCanonicalType(*SuperI);
+      // SubT must be SuperT or derived from it, or pointer or reference to
+      // such types.
+      if (const ReferenceType *RefTy = CanonicalSuperT->getAsReferenceType())
+        CanonicalSuperT = RefTy->getPointeeType();
+      if (SubIsPointer) {
+        if (const PointerType *PtrTy = CanonicalSuperT->getAsPointerType())
+          CanonicalSuperT = PtrTy->getPointeeType();
+        else {
+          continue;
+        }
+      }
+      CanonicalSuperT.setCVRQualifiers(0);
+      // If the types are the same, move on to the next type in the subset.
+      if (CanonicalSubT == CanonicalSuperT) {
+        Contained = true;
+        break;
+      }
+
+      // Otherwise we need to check the inheritance.
+      if (!SubIsClass || !CanonicalSuperT->isRecordType())
+        continue;
+
+      Paths.clear();
+      if (!IsDerivedFrom(CanonicalSubT, CanonicalSuperT, Paths))
+        continue;
+
+      if (Paths.isAmbiguous(CanonicalSuperT))
+        continue;
+
+      // FIXME: Check base access. Don't forget to enable path recording.
+
+      Contained = true;
+      break;
+    }
+    if (!Contained) {
+      Diag(SubLoc, DiagID);
+      Diag(SuperLoc, NoteID);
+      return true;
+    }
+  }
+  // We've run the gauntlet.
+  return false;
+}
+
 /// ObjCGetTypeForMethodDefinition - Builds the type for a method definition
 /// declarator
 QualType Sema::ObjCGetTypeForMethodDefinition(DeclPtrTy D) {
index 5eba26e70c6f8b25766cbbce1119740743d6fa99..dd12f3032fb1b52fcb79dfd20d9bf014c0e4705e 100644 (file)
@@ -61,3 +61,54 @@ void r7() throw(float); // expected-error {{exception specification in declarati
 // Top-level const doesn't matter.
 void r8() throw(int);
 void r8() throw(const int);
+
+struct A
+{
+};
+
+struct B1 : A
+{
+};
+
+struct B2 : A
+{
+};
+
+struct D : B1, B2
+{
+};
+
+struct Base
+{
+  virtual void f1() throw();
+  virtual void f2();
+  virtual void f3() throw(...);
+  virtual void f4() throw(int, float);
+
+  virtual void f5() throw(int, float);
+  virtual void f6() throw(A);
+  virtual void f7() throw(A, int, float);
+  virtual void f8();
+
+  virtual void g1() throw(); // expected-note {{overridden virtual function is here}}
+  virtual void g2() throw(int); // expected-note {{overridden virtual function is here}}
+  virtual void g3() throw(A); // expected-note {{overridden virtual function is here}}
+  virtual void g4() throw(B1); // expected-note {{overridden virtual function is here}}
+};
+struct Derived : Base
+{
+  virtual void f1() throw();
+  virtual void f2() throw(...);
+  virtual void f3();
+  virtual void f4() throw(float, int);
+
+  virtual void f5() throw(float);
+  virtual void f6() throw(B1);
+  virtual void f7() throw(B1, B2, int);
+  virtual void f8() throw(B2, B2, int, float, char, double, bool);
+
+  virtual void g1() throw(int); // expected-error {{exception specification of overriding function is more lax}}
+  virtual void g2(); // expected-error {{exception specification of overriding function is more lax}}
+  virtual void g3() throw(D); // expected-error {{exception specification of overriding function is more lax}}
+  virtual void g4() throw(A); // expected-error {{exception specification of overriding function is more lax}}
+};