[c++1z] Improve support for -fno-exceptions: we can't just ignore exception

specifications in this mode in C++17, since they're part of the function type,
so check and diagnose them like we would if exceptions were enabled.

Better ideas welcome.

llvm-svn: 288220
This commit is contained in:
Richard Smith 2016-11-30 00:13:55 +00:00
parent f9b191f135
commit 13b40bcc03
6 changed files with 62 additions and 50 deletions

View File

@ -1306,6 +1306,8 @@ def err_mismatched_exception_spec : Error<
"exception specification in declaration does not match previous declaration">;
def ext_mismatched_exception_spec : ExtWarn<err_mismatched_exception_spec.Text>,
InGroup<MicrosoftExceptionSpec>;
def warn_mismatched_exception_spec_no_exceptions : ExtWarn<err_mismatched_exception_spec.Text>,
InGroup<DiagGroup<"exception-spec-no-exceptions">>;
def err_override_exception_spec : Error<
"exception specification of overriding function is more lax than "
"base version">;

View File

@ -1330,11 +1330,7 @@ public:
bool CheckEquivalentExceptionSpec(
const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID,
const FunctionProtoType *Old, SourceLocation OldLoc,
const FunctionProtoType *New, SourceLocation NewLoc,
bool *MissingExceptionSpecification = nullptr,
bool *MissingEmptyExceptionSpecification = nullptr,
bool AllowNoexceptAllMatchWithNoSpec = false,
bool IsOperatorNew = false);
const FunctionProtoType *New, SourceLocation NewLoc);
bool CheckExceptionSpecSubset(const PartialDiagnostic &DiagID,
const PartialDiagnostic &NestedDiagID,
const PartialDiagnostic &NoteID,

View File

@ -2949,15 +2949,6 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD,
// but do not necessarily update the type of New.
if (CheckEquivalentExceptionSpec(Old, New))
return true;
// If exceptions are disabled, we might not have resolved the exception spec
// of one or both declarations. Do so now in C++1z, so that we can properly
// compare the types.
if (getLangOpts().CPlusPlus1z) {
for (QualType T : {Old->getType(), New->getType()})
if (auto *FPT = T->getAs<FunctionProtoType>())
if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
ResolveExceptionSpec(New->getLocation(), FPT);
}
OldQType = Context.getCanonicalType(Old->getType());
NewQType = Context.getCanonicalType(New->getType());

View File

@ -207,6 +207,14 @@ Sema::UpdateExceptionSpec(FunctionDecl *FD,
Context.adjustExceptionSpec(cast<FunctionDecl>(Redecl), ESI);
}
static bool CheckEquivalentExceptionSpecImpl(
Sema &S, const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID,
const FunctionProtoType *Old, SourceLocation OldLoc,
const FunctionProtoType *New, SourceLocation NewLoc,
bool *MissingExceptionSpecification = nullptr,
bool *MissingEmptyExceptionSpecification = nullptr,
bool AllowNoexceptAllMatchWithNoSpec = false, bool IsOperatorNew = false);
/// Determine whether a function has an implicitly-generated exception
/// specification.
static bool hasImplicitExceptionSpec(FunctionDecl *Decl) {
@ -229,6 +237,12 @@ static bool hasImplicitExceptionSpec(FunctionDecl *Decl) {
}
bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
// Just completely ignore this under -fno-exceptions prior to C++1z.
// In C++1z onwards, the exception specification is part of the type and
// we will diagnose mismatches anyway, so it's better to check for them here.
if (!getLangOpts().CXXExceptions && !getLangOpts().CPlusPlus1z)
return false;
OverloadedOperatorKind OO = New->getDeclName().getCXXOverloadedOperator();
bool IsOperatorNew = OO == OO_New || OO == OO_Array_New;
bool MissingExceptionSpecification = false;
@ -243,8 +257,8 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
// Check the types as written: they must match before any exception
// specification adjustment is applied.
if (!CheckEquivalentExceptionSpec(
PDiag(DiagID), PDiag(diag::note_previous_declaration),
if (!CheckEquivalentExceptionSpecImpl(
*this, PDiag(DiagID), PDiag(diag::note_previous_declaration),
Old->getType()->getAs<FunctionProtoType>(), Old->getLocation(),
New->getType()->getAs<FunctionProtoType>(), New->getLocation(),
&MissingExceptionSpecification, &MissingEmptyExceptionSpecification,
@ -395,11 +409,15 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
bool Sema::CheckEquivalentExceptionSpec(
const FunctionProtoType *Old, SourceLocation OldLoc,
const FunctionProtoType *New, SourceLocation NewLoc) {
if (!getLangOpts().CXXExceptions)
return false;
unsigned DiagID = diag::err_mismatched_exception_spec;
if (getLangOpts().MicrosoftExt)
DiagID = diag::ext_mismatched_exception_spec;
bool Result = CheckEquivalentExceptionSpec(PDiag(DiagID),
PDiag(diag::note_previous_declaration), Old, OldLoc, New, NewLoc);
bool Result = CheckEquivalentExceptionSpecImpl(
*this, PDiag(DiagID), PDiag(diag::note_previous_declaration),
Old, OldLoc, New, NewLoc);
// In Microsoft mode, mismatching exception specifications just cause a warning.
if (getLangOpts().MicrosoftExt)
@ -413,30 +431,23 @@ bool Sema::CheckEquivalentExceptionSpec(
/// \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,
SourceLocation OldLoc,
const FunctionProtoType *New,
SourceLocation NewLoc,
bool *MissingExceptionSpecification,
bool*MissingEmptyExceptionSpecification,
bool AllowNoexceptAllMatchWithNoSpec,
bool IsOperatorNew) {
// Just completely ignore this under -fno-exceptions.
if (!getLangOpts().CXXExceptions)
return false;
static bool CheckEquivalentExceptionSpecImpl(
Sema &S, const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID,
const FunctionProtoType *Old, SourceLocation OldLoc,
const FunctionProtoType *New, SourceLocation NewLoc,
bool *MissingExceptionSpecification,
bool *MissingEmptyExceptionSpecification,
bool AllowNoexceptAllMatchWithNoSpec, bool IsOperatorNew) {
if (MissingExceptionSpecification)
*MissingExceptionSpecification = false;
if (MissingEmptyExceptionSpecification)
*MissingEmptyExceptionSpecification = false;
Old = ResolveExceptionSpec(NewLoc, Old);
Old = S.ResolveExceptionSpec(NewLoc, Old);
if (!Old)
return false;
New = ResolveExceptionSpec(NewLoc, New);
New = S.ResolveExceptionSpec(NewLoc, New);
if (!New)
return false;
@ -470,8 +481,8 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
if (OldEST == EST_None && NewEST == EST_None)
return false;
FunctionProtoType::NoexceptResult OldNR = Old->getNoexceptSpec(Context);
FunctionProtoType::NoexceptResult NewNR = New->getNoexceptSpec(Context);
FunctionProtoType::NoexceptResult OldNR = Old->getNoexceptSpec(S.Context);
FunctionProtoType::NoexceptResult NewNR = New->getNoexceptSpec(S.Context);
if (OldNR == FunctionProtoType::NR_BadNoexcept ||
NewNR == FunctionProtoType::NR_BadNoexcept)
return false;
@ -486,9 +497,9 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
if (OldNR != NewNR &&
OldNR != FunctionProtoType::NR_NoNoexcept &&
NewNR != FunctionProtoType::NR_NoNoexcept) {
Diag(NewLoc, DiagID);
S.Diag(NewLoc, DiagID);
if (NoteID.getDiagID() != 0 && OldLoc.isValid())
Diag(OldLoc, NoteID);
S.Diag(OldLoc, NoteID);
return true;
}
@ -526,7 +537,7 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
// As a special compatibility feature, under C++0x we accept no spec and
// throw(std::bad_alloc) as equivalent for operator new and operator new[].
// This is because the implicit declaration changed, but old code would break.
if (getLangOpts().CPlusPlus11 && IsOperatorNew) {
if (S.getLangOpts().CPlusPlus11 && IsOperatorNew) {
const FunctionProtoType *WithExceptions = nullptr;
if (OldEST == EST_None && NewEST == EST_Dynamic)
WithExceptions = New;
@ -567,9 +578,9 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
return true;
}
Diag(NewLoc, DiagID);
S.Diag(NewLoc, DiagID);
if (NoteID.getDiagID() != 0 && OldLoc.isValid())
Diag(OldLoc, NoteID);
S.Diag(OldLoc, NoteID);
return true;
}
@ -581,11 +592,11 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
// to the second.
llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes;
for (const auto &I : Old->exceptions())
OldTypes.insert(Context.getCanonicalType(I).getUnqualifiedType());
OldTypes.insert(S.Context.getCanonicalType(I).getUnqualifiedType());
for (const auto &I : New->exceptions()) {
CanQualType TypePtr = Context.getCanonicalType(I).getUnqualifiedType();
if(OldTypes.count(TypePtr))
CanQualType TypePtr = S.Context.getCanonicalType(I).getUnqualifiedType();
if (OldTypes.count(TypePtr))
NewTypes.insert(TypePtr);
else
Success = false;
@ -596,12 +607,24 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
if (Success) {
return false;
}
Diag(NewLoc, DiagID);
S.Diag(NewLoc, DiagID);
if (NoteID.getDiagID() != 0 && OldLoc.isValid())
Diag(OldLoc, NoteID);
S.Diag(OldLoc, NoteID);
return true;
}
bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
const PartialDiagnostic &NoteID,
const FunctionProtoType *Old,
SourceLocation OldLoc,
const FunctionProtoType *New,
SourceLocation NewLoc) {
if (!getLangOpts().CXXExceptions)
return false;
return CheckEquivalentExceptionSpecImpl(*this, DiagID, NoteID, Old, OldLoc,
New, NewLoc);
}
/// 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.

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -isystem %S/Inputs -fsyntax-only -verify %s
// RUN: %clang_cc1 -isystem %S/Inputs -fsyntax-only -verify -std=c++1z %s
// expected-no-diagnostics
#include <malloc.h>

View File

@ -10,10 +10,9 @@ template<typename T> void redecl1() noexcept(noexcept(T())) {} // expected-error
template<bool A, bool B> void redecl2() noexcept(A); // expected-note {{previous}}
template<bool A, bool B> void redecl2() noexcept(B); // expected-error {{conflicting types}}
// These have the same canonical type.
// FIXME: It's not clear whether this is supposed to be valid.
template<typename A, typename B> void redecl3() throw(A);
template<typename A, typename B> void redecl3() throw(B);
// These have the same canonical type, but are still different.
template<typename A, typename B> void redecl3() throw(A); // expected-note {{previous}}
template<typename A, typename B> void redecl3() throw(B); // expected-error {{does not match previous}}
typedef int I;
template<bool B> void redecl4(I) noexcept(B);