diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 14 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaExceptionSpec.cpp | 55 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 16 | ||||
-rw-r--r-- | test/CXX/except/except.spec/p15.cpp | 14 | ||||
-rw-r--r-- | test/CXX/except/except.spec/p4.cpp | 36 | ||||
-rw-r--r-- | test/CXX/special/class.dtor/p3.cpp | 4 |
8 files changed, 103 insertions, 42 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index b2779d5cb8..1e93f1bc0d 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5425,6 +5425,10 @@ def err_out_of_line_default_deletes : Error< "defaulting this %select{default constructor|copy constructor|move " "constructor|copy assignment operator|move assignment operator|destructor}0 " "would delete it after its first declaration">; +def ext_implicit_exception_spec_mismatch : ExtWarn< + "function previously declared with an %select{explicit|implicit}0 exception " + "specification redeclared with an %select{implicit|explicit}0 exception " + "specification">, InGroup<DiagGroup<"implicit-exception-spec-mismatch">>; def warn_ptr_arith_precedes_bounds : Warning< "the pointer decremented by %0 refers before the beginning of the array">, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f55415996b..162c50b7fe 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5519,6 +5519,20 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, diag::err_static_out_of_line) << FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc()); } + + // C++11 [except.spec]p15: + // A deallocation function with no exception-specification is treated + // as if it were specified with noexcept(true). + const FunctionProtoType *FPT = R->getAs<FunctionProtoType>(); + if ((Name.getCXXOverloadedOperator() == OO_Delete || + Name.getCXXOverloadedOperator() == OO_Array_Delete) && + getLangOpts().CPlusPlus0x && FPT && !FPT->hasExceptionSpec()) { + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); + EPI.ExceptionSpecType = EST_BasicNoexcept; + NewFD->setType(Context.getFunctionType(FPT->getResultType(), + FPT->arg_type_begin(), + FPT->getNumArgs(), EPI)); + } } // Filter out previous declarations that don't match the scope. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 0adcfc1520..f0bd810574 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -9379,7 +9379,7 @@ CheckOperatorNewDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) { } static bool -CheckOperatorDeleteDeclaration(Sema &SemaRef, const FunctionDecl *FnDecl) { +CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) { // C++ [basic.stc.dynamic.deallocation]p1: // A program is ill-formed if deallocation functions are declared in a // namespace scope other than global scope or declared static in global diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index e35badc9f3..e1f4888d63 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -120,21 +120,22 @@ Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) { return SourceDecl->getType()->castAs<FunctionProtoType>(); } -/// Get the type that a function had prior to adjustment of the exception +/// Determine whether a function has an implicitly-generated exception /// specification. -static const FunctionProtoType *getUnadjustedFunctionType(FunctionDecl *Decl) { - if (isa<CXXDestructorDecl>(Decl) && Decl->getTypeSourceInfo()) { - const FunctionProtoType *Ty = - Decl->getTypeSourceInfo()->getType()->getAs<FunctionProtoType>(); - if (!Ty->hasExceptionSpec()) - // The type will be adjusted. Use the EST_None exception specification - // from the type as written. - return Ty; - } +static bool hasImplicitExceptionSpec(FunctionDecl *Decl) { + if (!isa<CXXDestructorDecl>(Decl) && + Decl->getDeclName().getCXXOverloadedOperator() != OO_Delete && + Decl->getDeclName().getCXXOverloadedOperator() != OO_Array_Delete) + return false; + + // If the user didn't declare the function, its exception specification must + // be implicit. + if (!Decl->getTypeSourceInfo()) + return true; - // Use whatever type the function now has. The TypeSourceInfo does not contain - // an instantiated exception specification for a function template, - return Decl->getType()->getAs<FunctionProtoType>(); + const FunctionProtoType *Ty = + Decl->getTypeSourceInfo()->getType()->getAs<FunctionProtoType>(); + return !Ty->hasExceptionSpec(); } bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { @@ -150,18 +151,31 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { // specification adjustment is applied. if (!CheckEquivalentExceptionSpec( PDiag(DiagID), PDiag(diag::note_previous_declaration), - getUnadjustedFunctionType(Old), Old->getLocation(), - getUnadjustedFunctionType(New), New->getLocation(), + Old->getType()->getAs<FunctionProtoType>(), Old->getLocation(), + New->getType()->getAs<FunctionProtoType>(), New->getLocation(), &MissingExceptionSpecification, &MissingEmptyExceptionSpecification, - /*AllowNoexceptAllMatchWithNoSpec=*/true, IsOperatorNew)) + /*AllowNoexceptAllMatchWithNoSpec=*/true, IsOperatorNew)) { + // C++11 [except.spec]p4 [DR1492]: + // If a declaration of a function has an implicit + // exception-specification, other declarations of the function shall + // not specify an exception-specification. + if (getLangOpts().CPlusPlus0x && + hasImplicitExceptionSpec(Old) != hasImplicitExceptionSpec(New)) { + Diag(New->getLocation(), diag::ext_implicit_exception_spec_mismatch) + << hasImplicitExceptionSpec(Old); + if (!Old->getLocation().isInvalid()) + Diag(Old->getLocation(), diag::note_previous_declaration); + } return false; + } // The failure was something other than an empty exception // specification; return an error. if (!MissingExceptionSpecification && !MissingEmptyExceptionSpecification) return true; - const FunctionProtoType *NewProto = getUnadjustedFunctionType(New); + const FunctionProtoType *NewProto = + New->getType()->getAs<FunctionProtoType>(); // The new function declaration is only missing an empty exception // specification "throw()". If the throw() specification came from a @@ -186,7 +200,8 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { } if (MissingExceptionSpecification && NewProto) { - const FunctionProtoType *OldProto = getUnadjustedFunctionType(Old); + const FunctionProtoType *OldProto = + Old->getType()->getAs<FunctionProtoType>(); FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo(); EPI.ExceptionSpecType = OldProto->getExceptionSpecType(); @@ -310,6 +325,10 @@ bool Sema::CheckEquivalentExceptionSpec( /// CheckEquivalentExceptionSpec - Check if the two types have compatible /// exception specifications. See C++ [except.spec]p3. +/// +/// \return \c false if the exception specifications match, \c true if there is +/// a problem. If \c true is returned, either a diagnostic has already been +/// produced or \c *MissingExceptionSpecification is set to \c true. bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, const FunctionProtoType *Old, diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index a5c809d9f4..22da2164df 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2169,16 +2169,6 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, ASTContext &Context = S.Context; const LangOptions &LangOpts = S.getLangOpts(); - bool ImplicitlyNoexcept = false; - if (D.getName().getKind() == UnqualifiedId::IK_OperatorFunctionId && - LangOpts.CPlusPlus0x) { - OverloadedOperatorKind OO = D.getName().OperatorFunctionId.Operator; - /// In C++0x, deallocation functions (normal and array operator delete) - /// are implicitly noexcept. - if (OO == OO_Delete || OO == OO_Array_Delete) - ImplicitlyNoexcept = true; - } - // The name we're declaring, if any. DeclarationName Name; if (D.getIdentifier()) @@ -2578,12 +2568,6 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, Exceptions, EPI); - if (FTI.getExceptionSpecType() == EST_None && - ImplicitlyNoexcept && chunkIndex == 0) { - // Only the outermost chunk is marked noexcept, of course. - EPI.ExceptionSpecType = EST_BasicNoexcept; - } - T = Context.getFunctionType(T, ArgTys.data(), ArgTys.size(), EPI); } diff --git a/test/CXX/except/except.spec/p15.cpp b/test/CXX/except/except.spec/p15.cpp index 110ec3fa03..fcf12357f9 100644 --- a/test/CXX/except/except.spec/p15.cpp +++ b/test/CXX/except/except.spec/p15.cpp @@ -9,16 +9,20 @@ void f() { delete[] new int[1]; } -void operator delete(void*) noexcept; -void operator delete[](void*) noexcept; +void operator delete(void*); +void operator delete[](void*); + +static_assert(noexcept(operator delete(0)), ""); +static_assert(noexcept(operator delete[](0)), ""); // Same goes for explicit declarations. void operator delete(void*, float); -void operator delete(void*, float) noexcept; - void operator delete[](void*, float); -void operator delete[](void*, float) noexcept; + +static_assert(noexcept(operator delete(0, 0.f)), ""); +static_assert(noexcept(operator delete[](0, 0.f)), ""); // But explicit specs stay. void operator delete(void*, double) throw(int); // expected-note {{previous}} +static_assert(!noexcept(operator delete(0, 0.)), ""); void operator delete(void*, double) noexcept; // expected-error {{does not match}} diff --git a/test/CXX/except/except.spec/p4.cpp b/test/CXX/except/except.spec/p4.cpp new file mode 100644 index 0000000000..1bf7018803 --- /dev/null +++ b/test/CXX/except/except.spec/p4.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -std=c++11 %s -verify -fcxx-exceptions + +// We permit overriding an implicit exception specification with an explicit one +// as an extension, for compatibility with existing code. + +struct S { + void a(); // expected-note {{here}} + ~S(); // expected-note {{here}} + void operator delete(void*); // expected-note {{here}} +}; + +void S::a() noexcept {} // expected-error {{does not match previous}} +S::~S() noexcept {} // expected-warning {{function previously declared with an implicit exception specification redeclared with an explicit exception specification}} +void S::operator delete(void*) noexcept {} // expected-warning {{function previously declared with an implicit exception specification redeclared with an explicit exception specification}} + +struct T { + void a() noexcept; // expected-note {{here}} + ~T() noexcept; // expected-note {{here}} + void operator delete(void*) noexcept; // expected-note {{here}} +}; + +void T::a() {} // expected-warning {{missing exception specification 'noexcept'}} +T::~T() {} // expected-warning {{function previously declared with an explicit exception specification redeclared with an implicit exception specification}} +void T::operator delete(void*) {} // expected-warning {{function previously declared with an explicit exception specification redeclared with an implicit exception specification}} + + +// The extension does not extend to function templates. + +template<typename T> struct U { + T t; + ~U(); // expected-note {{here}} + void operator delete(void*); // expected-note {{here}} +}; + +template<typename T> U<T>::~U() noexcept(true) {} // expected-error {{exception specification in declaration does not match previous declaration}} +template<typename T> void U<T>::operator delete(void*) noexcept(false) {} // expected-error {{exception specification in declaration does not match previous declaration}} diff --git a/test/CXX/special/class.dtor/p3.cpp b/test/CXX/special/class.dtor/p3.cpp index c3a292d50c..6f4d5c7f31 100644 --- a/test/CXX/special/class.dtor/p3.cpp +++ b/test/CXX/special/class.dtor/p3.cpp @@ -4,10 +4,10 @@ // the exception specification adjustment occurs. namespace DR1492 { struct A { ~A(); }; // expected-note {{here}} - A::~A() noexcept {} // expected-error {{does not match previous declaration}} + A::~A() noexcept {} // expected-warning {{previously declared with an implicit exception specification}} struct B { ~B() noexcept; }; // expected-note {{here}} - B::~B() {} // expected-warning {{~B' is missing exception specification 'noexcept'}} + B::~B() {} // expected-warning {{previously declared with an explicit exception specification}} template<typename T> struct C { T t; |