From 4f4d7b5d8e5fd921a407f9e00fed793c6ac1c1fc Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Sat, 4 Jul 2009 11:39:00 +0000 Subject: [PATCH] Catch function redeclarations with incompatible exception specifications. llvm-svn: 74787 --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Sema/Sema.h | 4 ++ clang/lib/Sema/SemaDeclCXX.cpp | 6 +++ clang/lib/Sema/SemaType.cpp | 47 +++++++++++++++++++ clang/test/SemaCXX/exception-spec.cpp | 30 +++++++++++- 5 files changed, 88 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index af8999e305ea..b1a73d05a45f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -287,6 +287,8 @@ def err_distant_exception_spec : Error< def err_incomplete_in_exception_spec : Error< "%select{|pointer to |reference to }1incomplete type %0 is not allowed " "in exception specification">; +def err_mismatched_exception_spec : Error< + "exception specification in declaration does not match previous declaration">; // C++ access checking def err_class_redeclared_with_different_access : Error< diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 72fb0a82a02d..7af80c0261e4 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -86,6 +86,7 @@ namespace clang { class ObjCMethodDecl; class ObjCPropertyDecl; class ObjCContainerDecl; + class FunctionProtoType; class BasePaths; struct MemberLookupCriteria; class CXXTemporary; @@ -394,6 +395,9 @@ public: DeclarationName GetNameForDeclarator(Declarator &D); bool CheckSpecifiedExceptionType(QualType T, const SourceRange &Range); bool CheckDistantExceptionSpec(QualType T); + bool CheckEquivalentExceptionSpec( + const FunctionProtoType *Old, SourceLocation OldLoc, + const FunctionProtoType *New, SourceLocation NewLoc); QualType ObjCGetTypeForMethodDefinition(DeclPtrTy D); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index d03576226204..75ceb1916555 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -258,6 +258,12 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old) { } } + if (CheckEquivalentExceptionSpec( + Old->getType()->getAsFunctionProtoType(), Old->getLocation(), + New->getType()->getAsFunctionProtoType(), New->getLocation())) { + Invalid = true; + } + return Invalid; } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 7fa6d703c2af..3756df870c2c 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/Parse/DeclSpec.h" +#include "llvm/ADT/SmallPtrSet.h" using namespace clang; /// \brief Perform adjustment on the parameter type of a function. @@ -1063,6 +1064,13 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip, break; } case DeclaratorChunk::MemberPointer: + // Verify that we're not building a pointer to pointer to function with + // exception specification. + if (getLangOptions().CPlusPlus && CheckDistantExceptionSpec(T)) { + Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec); + D.setInvalidType(true); + // Build the type anyway. + } // The scope spec must refer to a class, or be dependent. QualType ClsType; if (isDependentScopeSpecifier(DeclType.Mem.Scope())) { @@ -1187,6 +1195,45 @@ bool Sema::CheckDistantExceptionSpec(QualType T) { return FnT->hasExceptionSpec(); } +/// CheckEquivalentExceptionSpec - Check if the two types have equivalent +/// exception specifications. Exception specifications are equivalent if +/// they allow exactly the same set of exception types. It does not matter how +/// that is achieved. See C++ [except.spec]p2. +bool Sema::CheckEquivalentExceptionSpec( + const FunctionProtoType *Old, SourceLocation OldLoc, + const FunctionProtoType *New, SourceLocation NewLoc) { + bool OldAny = !Old->hasExceptionSpec() || Old->hasAnyExceptionSpec(); + bool NewAny = !New->hasExceptionSpec() || New->hasAnyExceptionSpec(); + if (OldAny && NewAny) + return false; + if (OldAny || NewAny) { + Diag(NewLoc, diag::err_mismatched_exception_spec); + Diag(OldLoc, diag::note_previous_declaration); + return true; + } + + bool Success = true; + // Both have a definite exception spec. Collect the first set, then compare + // to the second. + llvm::SmallPtrSet Types; + for (FunctionProtoType::exception_iterator I = Old->exception_begin(), + E = Old->exception_end(); I != E; ++I) + Types.insert(Context.getCanonicalType(*I).getTypePtr()); + + for (FunctionProtoType::exception_iterator I = New->exception_begin(), + E = New->exception_end(); I != E && Success; ++I) + Success = Types.erase(Context.getCanonicalType(*I).getTypePtr()); + + Success = Success && Types.empty(); + + if (Success) { + return false; + } + Diag(NewLoc, diag::err_mismatched_exception_spec); + Diag(OldLoc, diag::note_previous_declaration); + return true; +} + /// ObjCGetTypeForMethodDefinition - Builds the type for a method definition /// declarator QualType Sema::ObjCGetTypeForMethodDefinition(DeclPtrTy D) { diff --git a/clang/test/SemaCXX/exception-spec.cpp b/clang/test/SemaCXX/exception-spec.cpp index ea02aac4915f..5eba26e70c6f 100644 --- a/clang/test/SemaCXX/exception-spec.cpp +++ b/clang/test/SemaCXX/exception-spec.cpp @@ -1,4 +1,4 @@ -// RUN: clang-cc -fsyntax-only -verify %s +// RUN: clang-cc -fsyntax-only -verify -fms-extensions %s // Straight from the standard: // Plain function with spec @@ -33,3 +33,31 @@ void ic2() throw(Incomplete); // expected-error {{incomplete type 'struct Incomp void ic3() throw(void*); void ic4() throw(Incomplete*); // expected-error {{pointer to incomplete type 'struct Incomplete' is not allowed in exception specification}} void ic5() throw(Incomplete&); // expected-error {{reference to incomplete type 'struct Incomplete' is not allowed in exception specification}} + +// Redeclarations +typedef int INT; +void r1() throw(int); +void r1() throw(int); + +void r2() throw(int); +void r2() throw(INT); + +// throw-any spec and no spec at all are semantically equivalent +void r3(); +void r3() throw(...); + +void r4() throw(int, float); +void r4() throw(float, int); + +void r5() throw(int); // expected-note {{previous declaration}} +void r5(); // expected-error {{exception specification in declaration does not match}} + +void r6() throw(...); // expected-note {{previous declaration}} +void r6() throw(int); // expected-error {{exception specification in declaration does not match}} + +void r7() throw(int); // expected-note {{previous declaration}} +void r7() throw(float); // expected-error {{exception specification in declaration does not match}} + +// Top-level const doesn't matter. +void r8() throw(int); +void r8() throw(const int);