From: Sebastian Redl Date: Tue, 7 Jul 2009 20:29:57 +0000 (+0000) Subject: Implement checking of exception spec compatibility for overriding virtual functions. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=23c7d061367dd2fc1631e867cffc3d6aae24e799;p=clang Implement checking of exception spec compatibility for overriding virtual functions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@74943 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 32f2d51958..50a4ff5327 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 4c213b6594..99d3566726 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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 diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 85210f049d..290ab5d708 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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(*I)) { - if (!CheckOverridingFunctionReturnType(NewMD, OldMD)) + if (!CheckOverridingFunctionReturnType(NewMD, OldMD) && + !CheckOverridingFunctionExceptionSpec(NewMD, OldMD)) NewMD->addOverriddenMethod(OldMD); } } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 75ceb19165..72adcc22d1 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -882,7 +882,7 @@ namespace { i != e; ++i) { // Traverse the record, looking for methods. if (CXXMethodDecl *MD = dyn_cast(*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 diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index da61e6eb9e..8dcf4e48f5 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -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) { diff --git a/test/SemaCXX/exception-spec.cpp b/test/SemaCXX/exception-spec.cpp index 5eba26e70c..dd12f3032f 100644 --- a/test/SemaCXX/exception-spec.cpp +++ b/test/SemaCXX/exception-spec.cpp @@ -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}} +};