Propagate the new exception information to FunctionProtoType.
Change the interface to expose the new information and deal with the enormous fallout. Introduce the new ExceptionSpecificationType value EST_DynamicNone to more easily deal with empty throw specifications. Update the tests for noexcept and fix the various bugs uncovered, such as lack of tentative parsing support. llvm-svn: 127537
This commit is contained in:
parent
f2a79d94e4
commit
fa453cfdc3
|
@ -2430,19 +2430,17 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionProtoType(QualType result, const QualType *args, unsigned numArgs,
|
FunctionProtoType(QualType result, const QualType *args, unsigned numArgs,
|
||||||
QualType canonical, const ExtProtoInfo &epi);
|
QualType canonical, const ExtProtoInfo &epi,
|
||||||
|
const ASTContext *Context);
|
||||||
|
|
||||||
/// NumArgs - The number of arguments this function has, not counting '...'.
|
/// NumArgs - The number of arguments this function has, not counting '...'.
|
||||||
unsigned NumArgs : 20;
|
unsigned NumArgs : 20;
|
||||||
|
|
||||||
/// NumExceptions - The number of types in the exception spec, if any.
|
/// NumExceptions - The number of types in the exception spec, if any.
|
||||||
unsigned NumExceptions : 10;
|
unsigned NumExceptions : 9;
|
||||||
|
|
||||||
/// HasExceptionSpec - Whether this function has an exception spec at all.
|
/// ExceptionSpecType - The type of exception specification this function has.
|
||||||
unsigned HasExceptionSpec : 1;
|
unsigned ExceptionSpecType : 3;
|
||||||
|
|
||||||
/// HasAnyExceptionSpec - Whether this function has a throw(...) spec.
|
|
||||||
unsigned HasAnyExceptionSpec : 1;
|
|
||||||
|
|
||||||
/// ArgInfo - There is an variable size array after the class in memory that
|
/// ArgInfo - There is an variable size array after the class in memory that
|
||||||
/// holds the argument types.
|
/// holds the argument types.
|
||||||
|
@ -2450,6 +2448,19 @@ private:
|
||||||
/// Exceptions - There is another variable size array after ArgInfo that
|
/// Exceptions - There is another variable size array after ArgInfo that
|
||||||
/// holds the exception types.
|
/// holds the exception types.
|
||||||
|
|
||||||
|
/// NoexceptExpr - Instead of Exceptions, there may be a single Expr* pointing
|
||||||
|
/// to the expression in the noexcept() specifier.
|
||||||
|
|
||||||
|
/// Context - If there is a NoexceptExpr, we need to store a pointer to the
|
||||||
|
/// ASTContext as well. We do it right after NoexceptExpr.
|
||||||
|
|
||||||
|
const ASTContext *getContext() const {
|
||||||
|
// Context sits after NoexceptExpr, one pointer past where the arguments end
|
||||||
|
if(getExceptionSpecType() == EST_ComputedNoexcept)
|
||||||
|
return *(reinterpret_cast<const ASTContext *const *>(arg_type_end()) + 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
friend class ASTContext; // ASTContext creates these.
|
friend class ASTContext; // ASTContext creates these.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -2463,32 +2474,62 @@ public:
|
||||||
ExtProtoInfo EPI;
|
ExtProtoInfo EPI;
|
||||||
EPI.ExtInfo = getExtInfo();
|
EPI.ExtInfo = getExtInfo();
|
||||||
EPI.Variadic = isVariadic();
|
EPI.Variadic = isVariadic();
|
||||||
EPI.ExceptionSpecType = hasExceptionSpec() ?
|
EPI.ExceptionSpecType = getExceptionSpecType();
|
||||||
(hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) : EST_None;
|
|
||||||
EPI.TypeQuals = static_cast<unsigned char>(getTypeQuals());
|
EPI.TypeQuals = static_cast<unsigned char>(getTypeQuals());
|
||||||
EPI.RefQualifier = getRefQualifier();
|
EPI.RefQualifier = getRefQualifier();
|
||||||
|
if (EPI.ExceptionSpecType == EST_Dynamic) {
|
||||||
EPI.NumExceptions = NumExceptions;
|
EPI.NumExceptions = NumExceptions;
|
||||||
EPI.Exceptions = exception_begin();
|
EPI.Exceptions = exception_begin();
|
||||||
|
} else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
|
||||||
|
EPI.NoexceptExpr = getNoexceptExpr();
|
||||||
|
}
|
||||||
return EPI;
|
return EPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasExceptionSpec() const { return HasExceptionSpec; }
|
/// \brief Get the kind of exception specification on this function.
|
||||||
bool hasAnyExceptionSpec() const { return HasAnyExceptionSpec; }
|
ExceptionSpecificationType getExceptionSpecType() const {
|
||||||
|
return static_cast<ExceptionSpecificationType>(ExceptionSpecType);
|
||||||
|
}
|
||||||
|
/// \brief Return whether this function has any kind of exception spec.
|
||||||
|
bool hasExceptionSpec() const {
|
||||||
|
return getExceptionSpecType() != EST_None;
|
||||||
|
}
|
||||||
|
/// \brief Return whether this function has a dynamic (throw) exception spec.
|
||||||
|
bool hasDynamicExceptionSpec() const {
|
||||||
|
return isDynamicExceptionSpec(getExceptionSpecType());
|
||||||
|
}
|
||||||
|
/// \brief Return whther this function has a noexcept exception spec.
|
||||||
|
bool hasNoexceptExceptionSpec() const {
|
||||||
|
return isNoexceptExceptionSpec(getExceptionSpecType());
|
||||||
|
}
|
||||||
|
/// \brief Result type of getNoexceptSpec().
|
||||||
|
enum NoexceptResult {
|
||||||
|
NR_NoNoexcept, ///< There is no noexcept specifier.
|
||||||
|
NR_BadNoexcept, ///< The noexcept specifier has a bad expression.
|
||||||
|
NR_Dependent, ///< The noexcept specifier is dependent.
|
||||||
|
NR_Throw, ///< The noexcept specifier evaluates to false.
|
||||||
|
NR_Nothrow ///< The noexcept specifier evaluates to true.
|
||||||
|
};
|
||||||
|
/// \brief Get the meaning of the noexcept spec on this function, if any.
|
||||||
|
NoexceptResult getNoexceptSpec() const;
|
||||||
unsigned getNumExceptions() const { return NumExceptions; }
|
unsigned getNumExceptions() const { return NumExceptions; }
|
||||||
QualType getExceptionType(unsigned i) const {
|
QualType getExceptionType(unsigned i) const {
|
||||||
assert(i < NumExceptions && "Invalid exception number!");
|
assert(i < NumExceptions && "Invalid exception number!");
|
||||||
return exception_begin()[i];
|
return exception_begin()[i];
|
||||||
}
|
}
|
||||||
bool hasEmptyExceptionSpec() const {
|
Expr *getNoexceptExpr() const {
|
||||||
return hasExceptionSpec() && !hasAnyExceptionSpec() &&
|
if (getExceptionSpecType() != EST_ComputedNoexcept)
|
||||||
getNumExceptions() == 0;
|
return 0;
|
||||||
|
// NoexceptExpr sits where the arguments end.
|
||||||
|
return *reinterpret_cast<Expr *const *>(arg_type_end());
|
||||||
}
|
}
|
||||||
|
bool isNothrow() const {
|
||||||
/// \brief Returns true if this function type has a non-throwing
|
ExceptionSpecificationType EST = getExceptionSpecType();
|
||||||
/// exception-specification.
|
if (EST == EST_DynamicNone || EST == EST_BasicNoexcept)
|
||||||
bool hasNonThrowingExceptionSpec() const {
|
return true;
|
||||||
// FIXME: this needs to be updated for C++0x.
|
if (EST != EST_ComputedNoexcept)
|
||||||
return hasEmptyExceptionSpec();
|
return false;
|
||||||
|
return getNoexceptSpec() == NR_Nothrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
using FunctionType::isVariadic;
|
using FunctionType::isVariadic;
|
||||||
|
@ -2521,6 +2562,8 @@ public:
|
||||||
return arg_type_end();
|
return arg_type_end();
|
||||||
}
|
}
|
||||||
exception_iterator exception_end() const {
|
exception_iterator exception_end() const {
|
||||||
|
if (getExceptionSpecType() != EST_Dynamic)
|
||||||
|
return exception_begin();
|
||||||
return exception_begin() + NumExceptions;
|
return exception_begin() + NumExceptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2535,7 +2578,7 @@ public:
|
||||||
void Profile(llvm::FoldingSetNodeID &ID);
|
void Profile(llvm::FoldingSetNodeID &ID);
|
||||||
static void Profile(llvm::FoldingSetNodeID &ID, QualType Result,
|
static void Profile(llvm::FoldingSetNodeID &ID, QualType Result,
|
||||||
arg_type_iterator ArgTys, unsigned NumArgs,
|
arg_type_iterator ArgTys, unsigned NumArgs,
|
||||||
const ExtProtoInfo &EPI);
|
const ExtProtoInfo &EPI, const ASTContext *Context);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -560,6 +560,8 @@ def err_deep_exception_specs_differ : Error<
|
||||||
"exception specifications of %select{return|argument}0 types differ">;
|
"exception specifications of %select{return|argument}0 types differ">;
|
||||||
def warn_missing_exception_specification : Warning<
|
def warn_missing_exception_specification : Warning<
|
||||||
"%0 is missing exception specification '%1'">;
|
"%0 is missing exception specification '%1'">;
|
||||||
|
def err_noexcept_needs_constant_expression : Error<
|
||||||
|
"argument to noexcept specifier must be a constant expression">;
|
||||||
|
|
||||||
// C++ access checking
|
// C++ access checking
|
||||||
def err_class_redeclared_with_different_access : Error<
|
def err_class_redeclared_with_different_access : Error<
|
||||||
|
|
|
@ -19,14 +19,15 @@ namespace clang {
|
||||||
/// \brief The various types of exception specifications that exist in C++0x.
|
/// \brief The various types of exception specifications that exist in C++0x.
|
||||||
enum ExceptionSpecificationType {
|
enum ExceptionSpecificationType {
|
||||||
EST_None, ///< no exception specification
|
EST_None, ///< no exception specification
|
||||||
EST_Dynamic, ///< throw() or throw(T1, T2)
|
EST_DynamicNone, ///< throw()
|
||||||
EST_DynamicAny, ///< Microsoft throw(...) extension
|
EST_Dynamic, ///< throw(T1, T2)
|
||||||
|
EST_MSAny, ///< Microsoft throw(...) extension
|
||||||
EST_BasicNoexcept, ///< noexcept
|
EST_BasicNoexcept, ///< noexcept
|
||||||
EST_ComputedNoexcept ///< noexcept(expression)
|
EST_ComputedNoexcept ///< noexcept(expression)
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool isDynamicExceptionSpec(ExceptionSpecificationType ESpecType) {
|
inline bool isDynamicExceptionSpec(ExceptionSpecificationType ESpecType) {
|
||||||
return ESpecType == EST_Dynamic || ESpecType == EST_DynamicAny;
|
return ESpecType >= EST_DynamicNone && ESpecType <= EST_MSAny;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType) {
|
inline bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType) {
|
||||||
|
|
|
@ -757,7 +757,8 @@ public:
|
||||||
const FunctionProtoType *Old, SourceLocation OldLoc,
|
const FunctionProtoType *Old, SourceLocation OldLoc,
|
||||||
const FunctionProtoType *New, SourceLocation NewLoc,
|
const FunctionProtoType *New, SourceLocation NewLoc,
|
||||||
bool *MissingExceptionSpecification = 0,
|
bool *MissingExceptionSpecification = 0,
|
||||||
bool *MissingEmptyExceptionSpecification = 0);
|
bool *MissingEmptyExceptionSpecification = 0,
|
||||||
|
bool AllowNoexceptAllMatchWithNoSpec = false);
|
||||||
bool CheckExceptionSpecSubset(
|
bool CheckExceptionSpecSubset(
|
||||||
const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID,
|
const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID,
|
||||||
const FunctionProtoType *Superset, SourceLocation SuperLoc,
|
const FunctionProtoType *Superset, SourceLocation SuperLoc,
|
||||||
|
@ -1076,8 +1077,9 @@ public:
|
||||||
bool MergeFunctionDecl(FunctionDecl *New, Decl *Old);
|
bool MergeFunctionDecl(FunctionDecl *New, Decl *Old);
|
||||||
bool MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old);
|
bool MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old);
|
||||||
void mergeObjCMethodDecls(ObjCMethodDecl *New, const ObjCMethodDecl *Old);
|
void mergeObjCMethodDecls(ObjCMethodDecl *New, const ObjCMethodDecl *Old);
|
||||||
void MergeVarDeclTypes(VarDecl *New, VarDecl *Old);
|
|
||||||
void MergeVarDecl(VarDecl *New, LookupResult &OldDecls);
|
void MergeVarDecl(VarDecl *New, LookupResult &OldDecls);
|
||||||
|
void MergeVarDeclTypes(VarDecl *New, VarDecl *Old);
|
||||||
|
void MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old);
|
||||||
bool MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old);
|
bool MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old);
|
||||||
|
|
||||||
// AssignmentAction - This is used by all the assignment diagnostic functions
|
// AssignmentAction - This is used by all the assignment diagnostic functions
|
||||||
|
|
|
@ -1909,7 +1909,7 @@ ASTContext::getFunctionType(QualType ResultTy,
|
||||||
// Unique functions, to guarantee there is only one function of a particular
|
// Unique functions, to guarantee there is only one function of a particular
|
||||||
// structure.
|
// structure.
|
||||||
llvm::FoldingSetNodeID ID;
|
llvm::FoldingSetNodeID ID;
|
||||||
FunctionProtoType::Profile(ID, ResultTy, ArgArray, NumArgs, EPI);
|
FunctionProtoType::Profile(ID, ResultTy, ArgArray, NumArgs, EPI, this);
|
||||||
|
|
||||||
void *InsertPos = 0;
|
void *InsertPos = 0;
|
||||||
if (FunctionProtoType *FTP =
|
if (FunctionProtoType *FTP =
|
||||||
|
@ -1953,14 +1953,20 @@ ASTContext::getFunctionType(QualType ResultTy,
|
||||||
|
|
||||||
// FunctionProtoType objects are allocated with extra bytes after them
|
// FunctionProtoType objects are allocated with extra bytes after them
|
||||||
// for two variable size arrays (for parameter and exception types) at the
|
// for two variable size arrays (for parameter and exception types) at the
|
||||||
// end of them.
|
// end of them. Instead of the exception types, there could be a noexcept
|
||||||
|
// expression and a context pointer.
|
||||||
size_t Size = sizeof(FunctionProtoType) +
|
size_t Size = sizeof(FunctionProtoType) +
|
||||||
NumArgs * sizeof(QualType) +
|
NumArgs * sizeof(QualType);
|
||||||
EPI.NumExceptions * sizeof(QualType);
|
if (EPI.ExceptionSpecType == EST_Dynamic)
|
||||||
|
Size += EPI.NumExceptions * sizeof(QualType);
|
||||||
|
else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
|
||||||
|
Size += sizeof(Expr*) + sizeof(ASTContext*);
|
||||||
|
}
|
||||||
FunctionProtoType *FTP = (FunctionProtoType*) Allocate(Size, TypeAlignment);
|
FunctionProtoType *FTP = (FunctionProtoType*) Allocate(Size, TypeAlignment);
|
||||||
FunctionProtoType::ExtProtoInfo newEPI = EPI;
|
FunctionProtoType::ExtProtoInfo newEPI = EPI;
|
||||||
newEPI.ExtInfo = EPI.ExtInfo.withCallingConv(CallConv);
|
newEPI.ExtInfo = EPI.ExtInfo.withCallingConv(CallConv);
|
||||||
new (FTP) FunctionProtoType(ResultTy, ArgArray, NumArgs, Canonical, newEPI);
|
new (FTP) FunctionProtoType(ResultTy, ArgArray, NumArgs, Canonical, newEPI,
|
||||||
|
this);
|
||||||
Types.push_back(FTP);
|
Types.push_back(FTP);
|
||||||
FunctionProtoTypes.InsertNode(FTP, InsertPos);
|
FunctionProtoTypes.InsertNode(FTP, InsertPos);
|
||||||
return QualType(FTP, 0);
|
return QualType(FTP, 0);
|
||||||
|
|
|
@ -521,10 +521,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
||||||
}
|
}
|
||||||
if (Proto1->isVariadic() != Proto2->isVariadic())
|
if (Proto1->isVariadic() != Proto2->isVariadic())
|
||||||
return false;
|
return false;
|
||||||
if (Proto1->hasExceptionSpec() != Proto2->hasExceptionSpec())
|
if (Proto1->getExceptionSpecType() != Proto2->getExceptionSpecType())
|
||||||
return false;
|
|
||||||
if (Proto1->hasAnyExceptionSpec() != Proto2->hasAnyExceptionSpec())
|
|
||||||
return false;
|
return false;
|
||||||
|
if (Proto1->getExceptionSpecType() == EST_Dynamic) {
|
||||||
if (Proto1->getNumExceptions() != Proto2->getNumExceptions())
|
if (Proto1->getNumExceptions() != Proto2->getNumExceptions())
|
||||||
return false;
|
return false;
|
||||||
for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) {
|
for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) {
|
||||||
|
@ -533,6 +532,12 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
||||||
Proto2->getExceptionType(I)))
|
Proto2->getExceptionType(I)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if (Proto1->getExceptionSpecType() == EST_ComputedNoexcept) {
|
||||||
|
if (!IsStructurallyEquivalent(Context,
|
||||||
|
Proto1->getNoexceptExpr(),
|
||||||
|
Proto2->getNoexceptExpr()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (Proto1->getTypeQuals() != Proto2->getTypeQuals())
|
if (Proto1->getTypeQuals() != Proto2->getTypeQuals())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -413,21 +413,31 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
|
||||||
Proto += " restrict";
|
Proto += " restrict";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FT && FT->hasExceptionSpec()) {
|
if (FT && FT->hasDynamicExceptionSpec()) {
|
||||||
Proto += " throw(";
|
Proto += " throw(";
|
||||||
if (FT->hasAnyExceptionSpec())
|
if (FT->getExceptionSpecType() == EST_MSAny)
|
||||||
Proto += "...";
|
Proto += "...";
|
||||||
else
|
else
|
||||||
for (unsigned I = 0, N = FT->getNumExceptions(); I != N; ++I) {
|
for (unsigned I = 0, N = FT->getNumExceptions(); I != N; ++I) {
|
||||||
if (I)
|
if (I)
|
||||||
Proto += ", ";
|
Proto += ", ";
|
||||||
|
|
||||||
|
|
||||||
std::string ExceptionType;
|
std::string ExceptionType;
|
||||||
FT->getExceptionType(I).getAsStringInternal(ExceptionType, SubPolicy);
|
FT->getExceptionType(I).getAsStringInternal(ExceptionType, SubPolicy);
|
||||||
Proto += ExceptionType;
|
Proto += ExceptionType;
|
||||||
}
|
}
|
||||||
Proto += ")";
|
Proto += ")";
|
||||||
|
} else if (FT && isNoexceptExceptionSpec(FT->getExceptionSpecType())) {
|
||||||
|
Proto += " noexcept";
|
||||||
|
if (FT->getExceptionSpecType() == EST_ComputedNoexcept) {
|
||||||
|
Proto += "(";
|
||||||
|
llvm::raw_string_ostream EOut(Proto);
|
||||||
|
FT->getNoexceptExpr()->printPretty(EOut, Context, 0, SubPolicy,
|
||||||
|
Indentation);
|
||||||
|
EOut.flush();
|
||||||
|
Proto += EOut.str();
|
||||||
|
Proto += ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (D->hasAttr<NoReturnAttr>())
|
if (D->hasAttr<NoReturnAttr>())
|
||||||
|
|
|
@ -975,15 +975,16 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
|
||||||
dispatch(*I);
|
dispatch(*I);
|
||||||
pop();
|
pop();
|
||||||
|
|
||||||
if (T->hasExceptionSpec()) {
|
if (T->hasDynamicExceptionSpec()) {
|
||||||
push("exception_specifiers");
|
push("exception_specifiers");
|
||||||
setFlag("any", T->hasAnyExceptionSpec());
|
setFlag("any", T->getExceptionSpecType() == EST_MSAny);
|
||||||
completeAttrs();
|
completeAttrs();
|
||||||
for (FunctionProtoType::exception_iterator
|
for (FunctionProtoType::exception_iterator
|
||||||
I = T->exception_begin(), E = T->exception_end(); I != E; ++I)
|
I = T->exception_begin(), E = T->exception_end(); I != E; ++I)
|
||||||
dispatch(*I);
|
dispatch(*I);
|
||||||
pop();
|
pop();
|
||||||
}
|
}
|
||||||
|
// FIXME: noexcept specifier
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitTemplateSpecializationTypeChildren(TemplateSpecializationType *T) {
|
void visitTemplateSpecializationTypeChildren(TemplateSpecializationType *T) {
|
||||||
|
|
|
@ -1659,7 +1659,7 @@ static Expr::CanThrowResult CanCalleeThrow(const Decl *D,
|
||||||
if (!FT)
|
if (!FT)
|
||||||
return Expr::CT_Can;
|
return Expr::CT_Can;
|
||||||
|
|
||||||
return FT->hasEmptyExceptionSpec() ? Expr::CT_Cannot : Expr::CT_Can;
|
return FT->isNothrow() ? Expr::CT_Cannot : Expr::CT_Can;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expr::CanThrowResult CanDynamicCastThrow(const CXXDynamicCastExpr *DC) {
|
static Expr::CanThrowResult CanDynamicCastThrow(const CXXDynamicCastExpr *DC) {
|
||||||
|
|
|
@ -102,7 +102,7 @@ void CXXNewExpr::AllocateArgsArray(ASTContext &C, bool isArray,
|
||||||
|
|
||||||
bool CXXNewExpr::shouldNullCheckAllocation() const {
|
bool CXXNewExpr::shouldNullCheckAllocation() const {
|
||||||
return getOperatorNew()->getType()->
|
return getOperatorNew()->getType()->
|
||||||
castAs<FunctionProtoType>()->hasNonThrowingExceptionSpec();
|
castAs<FunctionProtoType>()->isNothrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CXXDeleteExpr
|
// CXXDeleteExpr
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "clang/AST/PrettyPrinter.h"
|
#include "clang/AST/PrettyPrinter.h"
|
||||||
#include "clang/AST/TypeVisitor.h"
|
#include "clang/AST/TypeVisitor.h"
|
||||||
#include "clang/Basic/Specifiers.h"
|
#include "clang/Basic/Specifiers.h"
|
||||||
|
#include "llvm/ADT/APSInt.h"
|
||||||
#include "llvm/ADT/StringExtras.h"
|
#include "llvm/ADT/StringExtras.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -1165,7 +1166,8 @@ llvm::StringRef FunctionType::getNameForCallConv(CallingConv CC) {
|
||||||
|
|
||||||
FunctionProtoType::FunctionProtoType(QualType result, const QualType *args,
|
FunctionProtoType::FunctionProtoType(QualType result, const QualType *args,
|
||||||
unsigned numArgs, QualType canonical,
|
unsigned numArgs, QualType canonical,
|
||||||
const ExtProtoInfo &epi)
|
const ExtProtoInfo &epi,
|
||||||
|
const ASTContext *context)
|
||||||
: FunctionType(FunctionProto, result, epi.Variadic, epi.TypeQuals,
|
: FunctionType(FunctionProto, result, epi.Variadic, epi.TypeQuals,
|
||||||
epi.RefQualifier, canonical,
|
epi.RefQualifier, canonical,
|
||||||
result->isDependentType(),
|
result->isDependentType(),
|
||||||
|
@ -1173,8 +1175,7 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args,
|
||||||
result->containsUnexpandedParameterPack(),
|
result->containsUnexpandedParameterPack(),
|
||||||
epi.ExtInfo),
|
epi.ExtInfo),
|
||||||
NumArgs(numArgs), NumExceptions(epi.NumExceptions),
|
NumArgs(numArgs), NumExceptions(epi.NumExceptions),
|
||||||
HasExceptionSpec(isDynamicExceptionSpec(epi.ExceptionSpecType)),
|
ExceptionSpecType(epi.ExceptionSpecType)
|
||||||
HasAnyExceptionSpec(epi.ExceptionSpecType == EST_DynamicAny)
|
|
||||||
{
|
{
|
||||||
// Fill in the trailing argument array.
|
// Fill in the trailing argument array.
|
||||||
QualType *argSlot = reinterpret_cast<QualType*>(this+1);
|
QualType *argSlot = reinterpret_cast<QualType*>(this+1);
|
||||||
|
@ -1188,6 +1189,7 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args,
|
||||||
argSlot[i] = args[i];
|
argSlot[i] = args[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getExceptionSpecType() == EST_Dynamic) {
|
||||||
// Fill in the exception array.
|
// Fill in the exception array.
|
||||||
QualType *exnSlot = argSlot + numArgs;
|
QualType *exnSlot = argSlot + numArgs;
|
||||||
for (unsigned i = 0, e = epi.NumExceptions; i != e; ++i) {
|
for (unsigned i = 0, e = epi.NumExceptions; i != e; ++i) {
|
||||||
|
@ -1199,6 +1201,39 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args,
|
||||||
|
|
||||||
exnSlot[i] = epi.Exceptions[i];
|
exnSlot[i] = epi.Exceptions[i];
|
||||||
}
|
}
|
||||||
|
} else if (getExceptionSpecType() == EST_ComputedNoexcept) {
|
||||||
|
// Store the noexcept expression and context.
|
||||||
|
Expr **noexSlot = reinterpret_cast<Expr**>(argSlot + numArgs);
|
||||||
|
*noexSlot = epi.NoexceptExpr;
|
||||||
|
const ASTContext **contextSlot = const_cast<const ASTContext**>(
|
||||||
|
reinterpret_cast<ASTContext**>(noexSlot + 1));
|
||||||
|
*contextSlot = context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionProtoType::NoexceptResult
|
||||||
|
FunctionProtoType::getNoexceptSpec() const {
|
||||||
|
ExceptionSpecificationType est = getExceptionSpecType();
|
||||||
|
if (est == EST_BasicNoexcept)
|
||||||
|
return NR_Nothrow;
|
||||||
|
|
||||||
|
if (est != EST_ComputedNoexcept)
|
||||||
|
return NR_NoNoexcept;
|
||||||
|
|
||||||
|
Expr *noexceptExpr = getNoexceptExpr();
|
||||||
|
if (!noexceptExpr)
|
||||||
|
return NR_BadNoexcept;
|
||||||
|
if (noexceptExpr->isValueDependent())
|
||||||
|
return NR_Dependent;
|
||||||
|
|
||||||
|
llvm::APSInt value;
|
||||||
|
ASTContext& ctx = const_cast<ASTContext&>(*getContext());
|
||||||
|
bool isICE = noexceptExpr->isIntegerConstantExpr(value, ctx, 0,
|
||||||
|
/*evaluated*/false);
|
||||||
|
(void)isICE;
|
||||||
|
assert(isICE && "AST should not contain bad noexcept expressions.");
|
||||||
|
|
||||||
|
return value.getBoolValue() ? NR_Nothrow : NR_Throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FunctionProtoType::isTemplateVariadic() const {
|
bool FunctionProtoType::isTemplateVariadic() const {
|
||||||
|
@ -1211,23 +1246,27 @@ bool FunctionProtoType::isTemplateVariadic() const {
|
||||||
|
|
||||||
void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
|
void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
|
||||||
const QualType *ArgTys, unsigned NumArgs,
|
const QualType *ArgTys, unsigned NumArgs,
|
||||||
const ExtProtoInfo &epi) {
|
const ExtProtoInfo &epi,
|
||||||
|
const ASTContext *Context) {
|
||||||
ID.AddPointer(Result.getAsOpaquePtr());
|
ID.AddPointer(Result.getAsOpaquePtr());
|
||||||
for (unsigned i = 0; i != NumArgs; ++i)
|
for (unsigned i = 0; i != NumArgs; ++i)
|
||||||
ID.AddPointer(ArgTys[i].getAsOpaquePtr());
|
ID.AddPointer(ArgTys[i].getAsOpaquePtr());
|
||||||
ID.AddBoolean(epi.Variadic);
|
ID.AddBoolean(epi.Variadic);
|
||||||
ID.AddInteger(epi.TypeQuals);
|
ID.AddInteger(epi.TypeQuals);
|
||||||
ID.AddInteger(epi.RefQualifier);
|
ID.AddInteger(epi.RefQualifier);
|
||||||
if (isDynamicExceptionSpec(epi.ExceptionSpecType)) {
|
ID.AddInteger(epi.ExceptionSpecType);
|
||||||
ID.AddBoolean(epi.ExceptionSpecType == EST_DynamicAny);
|
if (epi.ExceptionSpecType == EST_Dynamic) {
|
||||||
for (unsigned i = 0; i != epi.NumExceptions; ++i)
|
for (unsigned i = 0; i != epi.NumExceptions; ++i)
|
||||||
ID.AddPointer(epi.Exceptions[i].getAsOpaquePtr());
|
ID.AddPointer(epi.Exceptions[i].getAsOpaquePtr());
|
||||||
|
} else if (epi.ExceptionSpecType == EST_ComputedNoexcept && epi.NoexceptExpr){
|
||||||
|
epi.NoexceptExpr->Profile(ID, *Context, true);
|
||||||
}
|
}
|
||||||
epi.ExtInfo.Profile(ID);
|
epi.ExtInfo.Profile(ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID) {
|
void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID) {
|
||||||
Profile(ID, getResultType(), arg_type_begin(), NumArgs, getExtProtoInfo());
|
Profile(ID, getResultType(), arg_type_begin(), NumArgs, getExtProtoInfo(),
|
||||||
|
getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
QualType TypedefType::desugar() const {
|
QualType TypedefType::desugar() const {
|
||||||
|
|
|
@ -422,9 +422,9 @@ void TypePrinter::printFunctionProto(const FunctionProtoType *T,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (T->hasExceptionSpec()) {
|
if (T->hasDynamicExceptionSpec()) {
|
||||||
S += " throw(";
|
S += " throw(";
|
||||||
if (T->hasAnyExceptionSpec())
|
if (T->getExceptionSpecType() == EST_MSAny)
|
||||||
S += "...";
|
S += "...";
|
||||||
else
|
else
|
||||||
for (unsigned I = 0, N = T->getNumExceptions(); I != N; ++I) {
|
for (unsigned I = 0, N = T->getNumExceptions(); I != N; ++I) {
|
||||||
|
@ -436,6 +436,16 @@ void TypePrinter::printFunctionProto(const FunctionProtoType *T,
|
||||||
S += ExceptionType;
|
S += ExceptionType;
|
||||||
}
|
}
|
||||||
S += ")";
|
S += ")";
|
||||||
|
} else if (isNoexceptExceptionSpec(T->getExceptionSpecType())) {
|
||||||
|
S += " noexcept";
|
||||||
|
if (T->getExceptionSpecType() == EST_ComputedNoexcept) {
|
||||||
|
S += "(";
|
||||||
|
llvm::raw_string_ostream EOut(S);
|
||||||
|
T->getNoexceptExpr()->printPretty(EOut, 0, Policy);
|
||||||
|
EOut.flush();
|
||||||
|
S += EOut.str();
|
||||||
|
S += ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print(T->getResultType(), S);
|
print(T->getResultType(), S);
|
||||||
|
|
|
@ -1128,7 +1128,7 @@ static bool CanThrow(Expr *E) {
|
||||||
const FunctionType *FT = Ty->getAs<FunctionType>();
|
const FunctionType *FT = Ty->getAs<FunctionType>();
|
||||||
if (FT) {
|
if (FT) {
|
||||||
if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT))
|
if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT))
|
||||||
if (Proto->hasEmptyExceptionSpec())
|
if (Proto->isNothrow())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -709,7 +709,7 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI,
|
||||||
FuncAttrs |= llvm::Attribute::NoUnwind;
|
FuncAttrs |= llvm::Attribute::NoUnwind;
|
||||||
else if (const FunctionDecl *Fn = dyn_cast<FunctionDecl>(TargetDecl)) {
|
else if (const FunctionDecl *Fn = dyn_cast<FunctionDecl>(TargetDecl)) {
|
||||||
const FunctionProtoType *FPT = Fn->getType()->getAs<FunctionProtoType>();
|
const FunctionProtoType *FPT = Fn->getType()->getAs<FunctionProtoType>();
|
||||||
if (FPT && FPT->hasEmptyExceptionSpec())
|
if (FPT && FPT->isNothrow())
|
||||||
FuncAttrs |= llvm::Attribute::NoUnwind;
|
FuncAttrs |= llvm::Attribute::NoUnwind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -449,9 +449,8 @@ void CodeGenFunction::EmitStartEHSpec(const Decl *D) {
|
||||||
if (Proto == 0)
|
if (Proto == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(!Proto->hasAnyExceptionSpec() && "function with parameter pack");
|
// FIXME: What about noexcept?
|
||||||
|
if (!Proto->hasDynamicExceptionSpec())
|
||||||
if (!Proto->hasExceptionSpec())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unsigned NumExceptions = Proto->getNumExceptions();
|
unsigned NumExceptions = Proto->getNumExceptions();
|
||||||
|
@ -477,7 +476,7 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
|
||||||
if (Proto == 0)
|
if (Proto == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!Proto->hasExceptionSpec())
|
if (!Proto->hasDynamicExceptionSpec())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
EHStack.popFilter();
|
EHStack.popFilter();
|
||||||
|
|
|
@ -982,7 +982,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
|
||||||
// exception spec; for this part, we inline
|
// exception spec; for this part, we inline
|
||||||
// CXXNewExpr::shouldNullCheckAllocation()) and we have an
|
// CXXNewExpr::shouldNullCheckAllocation()) and we have an
|
||||||
// interesting initializer.
|
// interesting initializer.
|
||||||
bool nullCheck = allocatorType->hasNonThrowingExceptionSpec() &&
|
bool nullCheck = allocatorType->isNothrow() &&
|
||||||
!(allocType->isPODType() && !E->hasInitializer());
|
!(allocType->isPODType() && !E->hasInitializer());
|
||||||
|
|
||||||
llvm::BasicBlock *nullCheckBB = 0;
|
llvm::BasicBlock *nullCheckBB = 0;
|
||||||
|
|
|
@ -2042,6 +2042,11 @@ Parser::MaybeParseExceptionSpecification(SourceRange &SpecificationRange,
|
||||||
SourceLocation LParenLoc = ConsumeParen();
|
SourceLocation LParenLoc = ConsumeParen();
|
||||||
NoexceptType = EST_ComputedNoexcept;
|
NoexceptType = EST_ComputedNoexcept;
|
||||||
NoexceptExpr = ParseConstantExpression();
|
NoexceptExpr = ParseConstantExpression();
|
||||||
|
// The argument must be contextually convertible to bool. We use
|
||||||
|
// ActOnBooleanCondition for this purpose.
|
||||||
|
if (!NoexceptExpr.isInvalid())
|
||||||
|
NoexceptExpr = Actions.ActOnBooleanCondition(getCurScope(), KeywordLoc,
|
||||||
|
NoexceptExpr.get());
|
||||||
SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
|
SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
|
||||||
NoexceptRange = SourceRange(KeywordLoc, RParenLoc);
|
NoexceptRange = SourceRange(KeywordLoc, RParenLoc);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2090,7 +2095,7 @@ ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification(
|
||||||
if (!Tok.is(tok::l_paren)) {
|
if (!Tok.is(tok::l_paren)) {
|
||||||
Diag(Tok, diag::err_expected_lparen_after) << "throw";
|
Diag(Tok, diag::err_expected_lparen_after) << "throw";
|
||||||
SpecificationRange.setEnd(SpecificationRange.getBegin());
|
SpecificationRange.setEnd(SpecificationRange.getBegin());
|
||||||
return EST_Dynamic;
|
return EST_DynamicNone;
|
||||||
}
|
}
|
||||||
SourceLocation LParenLoc = ConsumeParen();
|
SourceLocation LParenLoc = ConsumeParen();
|
||||||
|
|
||||||
|
@ -2102,7 +2107,7 @@ ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification(
|
||||||
Diag(EllipsisLoc, diag::ext_ellipsis_exception_spec);
|
Diag(EllipsisLoc, diag::ext_ellipsis_exception_spec);
|
||||||
SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
|
SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
|
||||||
SpecificationRange.setEnd(RParenLoc);
|
SpecificationRange.setEnd(RParenLoc);
|
||||||
return EST_DynamicAny;
|
return EST_MSAny;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the sequence of type-ids.
|
// Parse the sequence of type-ids.
|
||||||
|
@ -2132,7 +2137,7 @@ ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification(
|
||||||
}
|
}
|
||||||
|
|
||||||
SpecificationRange.setEnd(MatchRHSPunctuation(tok::r_paren, LParenLoc));
|
SpecificationRange.setEnd(MatchRHSPunctuation(tok::r_paren, LParenLoc));
|
||||||
return EST_Dynamic;
|
return Exceptions.empty() ? EST_DynamicNone : EST_Dynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ParseTrailingReturnType - Parse a trailing return type on a new-style
|
/// ParseTrailingReturnType - Parse a trailing return type on a new-style
|
||||||
|
|
|
@ -1238,6 +1238,16 @@ Parser::TPResult Parser::TryParseFunctionDeclarator() {
|
||||||
if (!SkipUntil(tok::r_paren))
|
if (!SkipUntil(tok::r_paren))
|
||||||
return TPResult::Error();
|
return TPResult::Error();
|
||||||
}
|
}
|
||||||
|
if (Tok.is(tok::kw_noexcept)) {
|
||||||
|
ConsumeToken();
|
||||||
|
// Possibly an expression as well.
|
||||||
|
if (Tok.is(tok::l_paren)) {
|
||||||
|
// Find the matching rparen.
|
||||||
|
ConsumeParen();
|
||||||
|
if (!SkipUntil(tok::r_paren))
|
||||||
|
return TPResult::Error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return TPResult::Ambiguous();
|
return TPResult::Ambiguous();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1552,8 +1552,8 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
|
||||||
mergeParamDeclAttributes(*ni, *oi, Context);
|
mergeParamDeclAttributes(*ni, *oi, Context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MergeVarDecl - We parsed a variable 'New' which has the same name and scope
|
/// MergeVarDeclTypes - We parsed a variable 'New' which has the same name and
|
||||||
/// as a previous declaration 'Old'. Figure out how to merge their types,
|
/// scope as a previous declaration 'Old'. Figure out how to merge their types,
|
||||||
/// emitting diagnostics as appropriate.
|
/// emitting diagnostics as appropriate.
|
||||||
///
|
///
|
||||||
/// Declarations using the auto type specifier (C++ [decl.spec.auto]) call back
|
/// Declarations using the auto type specifier (C++ [decl.spec.auto]) call back
|
||||||
|
@ -1570,8 +1570,10 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old) {
|
||||||
if (AT && !AT->isDeduced()) {
|
if (AT && !AT->isDeduced()) {
|
||||||
// We don't know what the new type is until the initializer is attached.
|
// We don't know what the new type is until the initializer is attached.
|
||||||
return;
|
return;
|
||||||
} else if (Context.hasSameType(New->getType(), Old->getType()))
|
} else if (Context.hasSameType(New->getType(), Old->getType())) {
|
||||||
return;
|
// These could still be something that needs exception specs checked.
|
||||||
|
return MergeVarDeclExceptionSpecs(New, Old);
|
||||||
|
}
|
||||||
// C++ [basic.link]p10:
|
// C++ [basic.link]p10:
|
||||||
// [...] the types specified by all declarations referring to a given
|
// [...] the types specified by all declarations referring to a given
|
||||||
// object or function shall be identical, except that declarations for an
|
// object or function shall be identical, except that declarations for an
|
||||||
|
|
|
@ -383,6 +383,48 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old) {
|
||||||
return Invalid;
|
return Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Merge the exception specifications of two variable declarations.
|
||||||
|
///
|
||||||
|
/// This is called when there's a redeclaration of a VarDecl. The function
|
||||||
|
/// checks if the redeclaration might have an exception specification and
|
||||||
|
/// validates compatibility and merges the specs if necessary.
|
||||||
|
void Sema::MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old) {
|
||||||
|
// Shortcut if exceptions are disabled.
|
||||||
|
if (!getLangOptions().CXXExceptions)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(Context.hasSameType(New->getType(), Old->getType()) &&
|
||||||
|
"Should only be called if types are otherwise the same.");
|
||||||
|
|
||||||
|
QualType NewType = New->getType();
|
||||||
|
QualType OldType = Old->getType();
|
||||||
|
|
||||||
|
// We're only interested in pointers and references to functions, as well
|
||||||
|
// as pointers to member functions.
|
||||||
|
if (const ReferenceType *R = NewType->getAs<ReferenceType>()) {
|
||||||
|
NewType = R->getPointeeType();
|
||||||
|
OldType = OldType->getAs<ReferenceType>()->getPointeeType();
|
||||||
|
} else if (const PointerType *P = NewType->getAs<PointerType>()) {
|
||||||
|
NewType = P->getPointeeType();
|
||||||
|
OldType = OldType->getAs<PointerType>()->getPointeeType();
|
||||||
|
} else if (const MemberPointerType *M = NewType->getAs<MemberPointerType>()) {
|
||||||
|
NewType = M->getPointeeType();
|
||||||
|
OldType = OldType->getAs<MemberPointerType>()->getPointeeType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NewType->isFunctionProtoType())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// There's lots of special cases for functions. For function pointers, system
|
||||||
|
// libraries are hopefully not as broken so that we don't need these
|
||||||
|
// workarounds.
|
||||||
|
if (CheckEquivalentExceptionSpec(
|
||||||
|
OldType->getAs<FunctionProtoType>(), Old->getLocation(),
|
||||||
|
NewType->getAs<FunctionProtoType>(), New->getLocation())) {
|
||||||
|
New->setInvalidDecl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// CheckCXXDefaultArguments - Verify that the default arguments for a
|
/// CheckCXXDefaultArguments - Verify that the default arguments for a
|
||||||
/// function declaration are well-formed according to C++
|
/// function declaration are well-formed according to C++
|
||||||
/// [dcl.fct.default].
|
/// [dcl.fct.default].
|
||||||
|
@ -2978,24 +3020,33 @@ namespace {
|
||||||
/// implicitly-declared special member functions.
|
/// implicitly-declared special member functions.
|
||||||
class ImplicitExceptionSpecification {
|
class ImplicitExceptionSpecification {
|
||||||
ASTContext &Context;
|
ASTContext &Context;
|
||||||
bool AllowsAllExceptions;
|
// We order exception specifications thus:
|
||||||
|
// noexcept is the most restrictive, but is only used in C++0x.
|
||||||
|
// throw() comes next.
|
||||||
|
// Then a throw(collected exceptions)
|
||||||
|
// Finally no specification.
|
||||||
|
// throw(...) is used instead if any called function uses it.
|
||||||
|
ExceptionSpecificationType ComputedEST;
|
||||||
llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
|
llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
|
||||||
llvm::SmallVector<QualType, 4> Exceptions;
|
llvm::SmallVector<QualType, 4> Exceptions;
|
||||||
|
|
||||||
public:
|
void ClearExceptions() {
|
||||||
explicit ImplicitExceptionSpecification(ASTContext &Context)
|
ExceptionsSeen.clear();
|
||||||
: Context(Context), AllowsAllExceptions(false) { }
|
Exceptions.clear();
|
||||||
|
|
||||||
/// \brief Whether the special member function should have any
|
|
||||||
/// exception specification at all.
|
|
||||||
bool hasExceptionSpecification() const {
|
|
||||||
return !AllowsAllExceptions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Whether the special member function should have a
|
public:
|
||||||
/// throw(...) exception specification (a Microsoft extension).
|
explicit ImplicitExceptionSpecification(ASTContext &Context)
|
||||||
bool hasAnyExceptionSpecification() const {
|
: Context(Context), ComputedEST(EST_BasicNoexcept) {
|
||||||
return false;
|
if (!Context.getLangOptions().CPlusPlus0x)
|
||||||
|
ComputedEST = EST_DynamicNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Get the computed exception specification type.
|
||||||
|
ExceptionSpecificationType getExceptionSpecType() const {
|
||||||
|
assert(ComputedEST != EST_ComputedNoexcept &&
|
||||||
|
"noexcept(expr) should not be a possible result");
|
||||||
|
return ComputedEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief The number of exceptions in the exception specification.
|
/// \brief The number of exceptions in the exception specification.
|
||||||
|
@ -3004,23 +3055,62 @@ namespace {
|
||||||
/// \brief The set of exceptions in the exception specification.
|
/// \brief The set of exceptions in the exception specification.
|
||||||
const QualType *data() const { return Exceptions.data(); }
|
const QualType *data() const { return Exceptions.data(); }
|
||||||
|
|
||||||
/// \brief Note that
|
/// \brief Integrate another called method into the collected data.
|
||||||
void CalledDecl(CXXMethodDecl *Method) {
|
void CalledDecl(CXXMethodDecl *Method) {
|
||||||
// If we already know that we allow all exceptions, do nothing.
|
// If we have an MSAny spec already, don't bother.
|
||||||
if (AllowsAllExceptions || !Method)
|
if (!Method || ComputedEST == EST_MSAny)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const FunctionProtoType *Proto
|
const FunctionProtoType *Proto
|
||||||
= Method->getType()->getAs<FunctionProtoType>();
|
= Method->getType()->getAs<FunctionProtoType>();
|
||||||
|
|
||||||
|
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
|
||||||
|
|
||||||
// If this function can throw any exceptions, make a note of that.
|
// If this function can throw any exceptions, make a note of that.
|
||||||
if (!Proto->hasExceptionSpec() || Proto->hasAnyExceptionSpec()) {
|
if (EST == EST_MSAny || EST == EST_None) {
|
||||||
AllowsAllExceptions = true;
|
ClearExceptions();
|
||||||
ExceptionsSeen.clear();
|
ComputedEST = EST;
|
||||||
Exceptions.clear();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this function has a basic noexcept, it doesn't affect the outcome.
|
||||||
|
if (EST == EST_BasicNoexcept)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If we have a throw-all spec at this point, ignore the function.
|
||||||
|
if (ComputedEST == EST_None)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If we're still at noexcept(true) and there's a nothrow() callee,
|
||||||
|
// change to that specification.
|
||||||
|
if (EST == EST_DynamicNone) {
|
||||||
|
if (ComputedEST == EST_BasicNoexcept)
|
||||||
|
ComputedEST = EST_DynamicNone;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check out noexcept specs.
|
||||||
|
if (EST == EST_ComputedNoexcept) {
|
||||||
|
FunctionProtoType::NoexceptResult NR = Proto->getNoexceptSpec();
|
||||||
|
assert(NR != FunctionProtoType::NR_NoNoexcept &&
|
||||||
|
"Must have noexcept result for EST_ComputedNoexcept.");
|
||||||
|
assert(NR != FunctionProtoType::NR_Dependent &&
|
||||||
|
"Should not generate implicit declarations for dependent cases, "
|
||||||
|
"and don't know how to handle them anyway.");
|
||||||
|
|
||||||
|
// noexcept(false) -> no spec on the new function
|
||||||
|
if (NR == FunctionProtoType::NR_Throw) {
|
||||||
|
ClearExceptions();
|
||||||
|
ComputedEST = EST_None;
|
||||||
|
}
|
||||||
|
// noexcept(true) won't change anything either.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(EST == EST_Dynamic && "EST case not considered earlier.");
|
||||||
|
assert(ComputedEST != EST_None &&
|
||||||
|
"Shouldn't collect exceptions when throw-all is guaranteed.");
|
||||||
|
ComputedEST = EST_Dynamic;
|
||||||
// Record the exceptions in this function's exception specification.
|
// Record the exceptions in this function's exception specification.
|
||||||
for (FunctionProtoType::exception_iterator E = Proto->exception_begin(),
|
for (FunctionProtoType::exception_iterator E = Proto->exception_begin(),
|
||||||
EEnd = Proto->exception_end();
|
EEnd = Proto->exception_end();
|
||||||
|
@ -4672,7 +4762,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
|
||||||
// exception-specification. [...]
|
// exception-specification. [...]
|
||||||
ImplicitExceptionSpecification ExceptSpec(Context);
|
ImplicitExceptionSpecification ExceptSpec(Context);
|
||||||
|
|
||||||
// Direct base-class destructors.
|
// Direct base-class constructors.
|
||||||
for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(),
|
for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(),
|
||||||
BEnd = ClassDecl->bases_end();
|
BEnd = ClassDecl->bases_end();
|
||||||
B != BEnd; ++B) {
|
B != BEnd; ++B) {
|
||||||
|
@ -4689,7 +4779,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Virtual base-class destructors.
|
// Virtual base-class constructors.
|
||||||
for (CXXRecordDecl::base_class_iterator B = ClassDecl->vbases_begin(),
|
for (CXXRecordDecl::base_class_iterator B = ClassDecl->vbases_begin(),
|
||||||
BEnd = ClassDecl->vbases_end();
|
BEnd = ClassDecl->vbases_end();
|
||||||
B != BEnd; ++B) {
|
B != BEnd; ++B) {
|
||||||
|
@ -4703,7 +4793,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field destructors.
|
// Field constructors.
|
||||||
for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
|
for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
|
||||||
FEnd = ClassDecl->field_end();
|
FEnd = ClassDecl->field_end();
|
||||||
F != FEnd; ++F) {
|
F != FEnd; ++F) {
|
||||||
|
@ -4720,9 +4810,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionProtoType::ExtProtoInfo EPI;
|
FunctionProtoType::ExtProtoInfo EPI;
|
||||||
EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
|
EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
|
||||||
(ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
|
|
||||||
EST_None;
|
|
||||||
EPI.NumExceptions = ExceptSpec.size();
|
EPI.NumExceptions = ExceptSpec.size();
|
||||||
EPI.Exceptions = ExceptSpec.data();
|
EPI.Exceptions = ExceptSpec.data();
|
||||||
|
|
||||||
|
@ -5000,9 +5088,7 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
|
||||||
|
|
||||||
// Create the actual destructor declaration.
|
// Create the actual destructor declaration.
|
||||||
FunctionProtoType::ExtProtoInfo EPI;
|
FunctionProtoType::ExtProtoInfo EPI;
|
||||||
EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
|
EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
|
||||||
(ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
|
|
||||||
EST_None;
|
|
||||||
EPI.NumExceptions = ExceptSpec.size();
|
EPI.NumExceptions = ExceptSpec.size();
|
||||||
EPI.Exceptions = ExceptSpec.data();
|
EPI.Exceptions = ExceptSpec.data();
|
||||||
QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, EPI);
|
QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, EPI);
|
||||||
|
@ -5400,9 +5486,7 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) {
|
||||||
// An implicitly-declared copy assignment operator is an inline public
|
// An implicitly-declared copy assignment operator is an inline public
|
||||||
// member of its class.
|
// member of its class.
|
||||||
FunctionProtoType::ExtProtoInfo EPI;
|
FunctionProtoType::ExtProtoInfo EPI;
|
||||||
EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
|
EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
|
||||||
(ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
|
|
||||||
EST_None;
|
|
||||||
EPI.NumExceptions = ExceptSpec.size();
|
EPI.NumExceptions = ExceptSpec.size();
|
||||||
EPI.Exceptions = ExceptSpec.data();
|
EPI.Exceptions = ExceptSpec.data();
|
||||||
DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
|
DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
|
||||||
|
@ -5864,9 +5948,7 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
|
||||||
// An implicitly-declared copy constructor is an inline public
|
// An implicitly-declared copy constructor is an inline public
|
||||||
// member of its class.
|
// member of its class.
|
||||||
FunctionProtoType::ExtProtoInfo EPI;
|
FunctionProtoType::ExtProtoInfo EPI;
|
||||||
EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
|
EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
|
||||||
(ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
|
|
||||||
EST_None;
|
|
||||||
EPI.NumExceptions = ExceptSpec.size();
|
EPI.NumExceptions = ExceptSpec.size();
|
||||||
EPI.Exceptions = ExceptSpec.data();
|
EPI.Exceptions = ExceptSpec.data();
|
||||||
DeclarationName Name
|
DeclarationName Name
|
||||||
|
|
|
@ -105,7 +105,8 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
|
||||||
New->getType()->getAs<FunctionProtoType>(),
|
New->getType()->getAs<FunctionProtoType>(),
|
||||||
New->getLocation(),
|
New->getLocation(),
|
||||||
&MissingExceptionSpecification,
|
&MissingExceptionSpecification,
|
||||||
&MissingEmptyExceptionSpecification))
|
&MissingEmptyExceptionSpecification,
|
||||||
|
/*AllowNoexceptAllMatchWithNoSpec=*/true))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The failure was something other than an empty exception
|
// The failure was something other than an empty exception
|
||||||
|
@ -129,8 +130,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
|
||||||
Context.getSourceManager().isInSystemHeader(Old->getLocation())) &&
|
Context.getSourceManager().isInSystemHeader(Old->getLocation())) &&
|
||||||
Old->isExternC()) {
|
Old->isExternC()) {
|
||||||
FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
|
FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
|
||||||
EPI.ExceptionSpecType = EST_Dynamic;
|
EPI.ExceptionSpecType = EST_DynamicNone;
|
||||||
EPI.NumExceptions = 0;
|
|
||||||
QualType NewType = Context.getFunctionType(NewProto->getResultType(),
|
QualType NewType = Context.getFunctionType(NewProto->getResultType(),
|
||||||
NewProto->arg_type_begin(),
|
NewProto->arg_type_begin(),
|
||||||
NewProto->getNumArgs(),
|
NewProto->getNumArgs(),
|
||||||
|
@ -144,11 +144,14 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
|
||||||
= Old->getType()->getAs<FunctionProtoType>();
|
= Old->getType()->getAs<FunctionProtoType>();
|
||||||
|
|
||||||
FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
|
FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
|
||||||
EPI.ExceptionSpecType = OldProto->hasExceptionSpec() ?
|
EPI.ExceptionSpecType = OldProto->getExceptionSpecType();
|
||||||
(OldProto->hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) :
|
if (EPI.ExceptionSpecType == EST_Dynamic) {
|
||||||
EST_None;
|
|
||||||
EPI.NumExceptions = OldProto->getNumExceptions();
|
EPI.NumExceptions = OldProto->getNumExceptions();
|
||||||
EPI.Exceptions = OldProto->exception_begin();
|
EPI.Exceptions = OldProto->exception_begin();
|
||||||
|
} else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
|
||||||
|
// FIXME: We can't just take the expression from the old prototype. It
|
||||||
|
// likely contains references to the old prototype's parameters.
|
||||||
|
}
|
||||||
|
|
||||||
// Update the type of the function with the appropriate exception
|
// Update the type of the function with the appropriate exception
|
||||||
// specification.
|
// specification.
|
||||||
|
@ -178,6 +181,12 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
|
||||||
// Warn about the lack of exception specification.
|
// Warn about the lack of exception specification.
|
||||||
llvm::SmallString<128> ExceptionSpecString;
|
llvm::SmallString<128> ExceptionSpecString;
|
||||||
llvm::raw_svector_ostream OS(ExceptionSpecString);
|
llvm::raw_svector_ostream OS(ExceptionSpecString);
|
||||||
|
switch (OldProto->getExceptionSpecType()) {
|
||||||
|
case EST_DynamicNone:
|
||||||
|
OS << "throw()";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EST_Dynamic: {
|
||||||
OS << "throw(";
|
OS << "throw(";
|
||||||
bool OnFirstException = true;
|
bool OnFirstException = true;
|
||||||
for (FunctionProtoType::exception_iterator E = OldProto->exception_begin(),
|
for (FunctionProtoType::exception_iterator E = OldProto->exception_begin(),
|
||||||
|
@ -192,6 +201,23 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
|
||||||
OS << E->getAsString(Context.PrintingPolicy);
|
OS << E->getAsString(Context.PrintingPolicy);
|
||||||
}
|
}
|
||||||
OS << ")";
|
OS << ")";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EST_BasicNoexcept:
|
||||||
|
OS << "noexcept";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EST_ComputedNoexcept:
|
||||||
|
OS << "noexcept(";
|
||||||
|
OldProto->getNoexceptExpr()->printPretty(OS, Context, 0,
|
||||||
|
Context.PrintingPolicy);
|
||||||
|
OS << ")";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false && "This spec type is compatible with none.");
|
||||||
|
}
|
||||||
OS.flush();
|
OS.flush();
|
||||||
|
|
||||||
SourceLocation FixItLoc;
|
SourceLocation FixItLoc;
|
||||||
|
@ -236,10 +262,8 @@ bool Sema::CheckEquivalentExceptionSpec(
|
||||||
Old, OldLoc, New, NewLoc);
|
Old, OldLoc, New, NewLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CheckEquivalentExceptionSpec - Check if the two types have equivalent
|
/// CheckEquivalentExceptionSpec - Check if the two types have compatible
|
||||||
/// exception specifications. Exception specifications are equivalent if
|
/// exception specifications. See C++ [except.spec]p3.
|
||||||
/// 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 PartialDiagnostic &DiagID,
|
bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
|
||||||
const PartialDiagnostic & NoteID,
|
const PartialDiagnostic & NoteID,
|
||||||
const FunctionProtoType *Old,
|
const FunctionProtoType *Old,
|
||||||
|
@ -247,7 +271,8 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
|
||||||
const FunctionProtoType *New,
|
const FunctionProtoType *New,
|
||||||
SourceLocation NewLoc,
|
SourceLocation NewLoc,
|
||||||
bool *MissingExceptionSpecification,
|
bool *MissingExceptionSpecification,
|
||||||
bool *MissingEmptyExceptionSpecification) {
|
bool*MissingEmptyExceptionSpecification,
|
||||||
|
bool AllowNoexceptAllMatchWithNoSpec) {
|
||||||
// Just completely ignore this under -fno-exceptions.
|
// Just completely ignore this under -fno-exceptions.
|
||||||
if (!getLangOptions().CXXExceptions)
|
if (!getLangOptions().CXXExceptions)
|
||||||
return false;
|
return false;
|
||||||
|
@ -258,29 +283,109 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
|
||||||
if (MissingEmptyExceptionSpecification)
|
if (MissingEmptyExceptionSpecification)
|
||||||
*MissingEmptyExceptionSpecification = false;
|
*MissingEmptyExceptionSpecification = false;
|
||||||
|
|
||||||
bool OldAny = !Old->hasExceptionSpec() || Old->hasAnyExceptionSpec();
|
// C++0x [except.spec]p3: Two exception-specifications are compatible if:
|
||||||
bool NewAny = !New->hasExceptionSpec() || New->hasAnyExceptionSpec();
|
// - both are non-throwing, regardless of their form,
|
||||||
if (getLangOptions().Microsoft) {
|
// - both have the form noexcept(constant-expression) and the constant-
|
||||||
// Treat throw(whatever) as throw(...) to be compatible with MS headers.
|
// expressions are equivalent,
|
||||||
if (New->hasExceptionSpec() && New->getNumExceptions() > 0)
|
// - one exception-specification is a noexcept-specification allowing all
|
||||||
NewAny = true;
|
// exceptions and the other is of the form throw(type-id-list), or
|
||||||
if (Old->hasExceptionSpec() && Old->getNumExceptions() > 0)
|
// - both are dynamic-exception-specifications that have the same set of
|
||||||
OldAny = true;
|
// adjusted types.
|
||||||
|
//
|
||||||
|
// C++0x [except.spec]p12: An exception-specifcation is non-throwing if it is
|
||||||
|
// of the form throw(), noexcept, or noexcept(constant-expression) where the
|
||||||
|
// constant-expression yields true.
|
||||||
|
//
|
||||||
|
// CWG 1073 Proposed resolution: Strike the third bullet above.
|
||||||
|
//
|
||||||
|
// C++0x [except.spec]p4: If any declaration of a function has an exception-
|
||||||
|
// specifier that is not a noexcept-specification allowing all exceptions,
|
||||||
|
// all declarations [...] of that function shall have a compatible
|
||||||
|
// exception-specification.
|
||||||
|
//
|
||||||
|
// That last point basically means that noexcept(false) matches no spec.
|
||||||
|
// It's considered when AllowNoexceptAllMatchWithNoSpec is true.
|
||||||
|
|
||||||
|
ExceptionSpecificationType OldEST = Old->getExceptionSpecType();
|
||||||
|
ExceptionSpecificationType NewEST = New->getExceptionSpecType();
|
||||||
|
|
||||||
|
// Shortcut the case where both have no spec.
|
||||||
|
if (OldEST == EST_None && NewEST == EST_None)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FunctionProtoType::NoexceptResult OldNR = Old->getNoexceptSpec();
|
||||||
|
FunctionProtoType::NoexceptResult NewNR = New->getNoexceptSpec();
|
||||||
|
if (OldNR == FunctionProtoType::NR_BadNoexcept ||
|
||||||
|
NewNR == FunctionProtoType::NR_BadNoexcept)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Dependent noexcept specifiers are compatible with each other, but nothing
|
||||||
|
// else.
|
||||||
|
// One noexcept is compatible with another if the argument is the same
|
||||||
|
if (OldNR == NewNR &&
|
||||||
|
OldNR != FunctionProtoType::NR_NoNoexcept &&
|
||||||
|
NewNR != FunctionProtoType::NR_NoNoexcept)
|
||||||
|
return false;
|
||||||
|
if (OldNR != NewNR &&
|
||||||
|
OldNR != FunctionProtoType::NR_NoNoexcept &&
|
||||||
|
NewNR != FunctionProtoType::NR_NoNoexcept) {
|
||||||
|
Diag(NewLoc, DiagID);
|
||||||
|
if (NoteID.getDiagID() != 0)
|
||||||
|
Diag(OldLoc, NoteID);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OldAny && NewAny)
|
if (getLangOptions().Microsoft) {
|
||||||
|
// Treat throw(whatever) as throw(...) to be compatible with MS headers.
|
||||||
|
if (OldEST == EST_Dynamic)
|
||||||
|
OldEST = EST_MSAny;
|
||||||
|
if (NewEST == EST_Dynamic)
|
||||||
|
NewEST = EST_MSAny;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The MS extension throw(...) is compatible with itself.
|
||||||
|
if (OldEST == EST_MSAny && NewEST == EST_MSAny)
|
||||||
return false;
|
return false;
|
||||||
if (OldAny || NewAny) {
|
|
||||||
|
// It's also compatible with no spec.
|
||||||
|
if ((OldEST == EST_None && NewEST == EST_MSAny) ||
|
||||||
|
(OldEST == EST_MSAny && NewEST == EST_None))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// It's also compatible with noexcept(false).
|
||||||
|
if (OldEST == EST_MSAny && NewNR == FunctionProtoType::NR_Throw)
|
||||||
|
return false;
|
||||||
|
if (NewEST == EST_MSAny && OldNR == FunctionProtoType::NR_Throw)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// As described above, noexcept(false) matches no spec only for functions.
|
||||||
|
if (AllowNoexceptAllMatchWithNoSpec) {
|
||||||
|
if (OldEST == EST_None && NewNR == FunctionProtoType::NR_Throw)
|
||||||
|
return false;
|
||||||
|
if (NewEST == EST_None && OldNR == FunctionProtoType::NR_Throw)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any non-throwing specifications are compatible.
|
||||||
|
bool OldNonThrowing = OldNR == FunctionProtoType::NR_Nothrow ||
|
||||||
|
OldEST == EST_DynamicNone;
|
||||||
|
bool NewNonThrowing = NewNR == FunctionProtoType::NR_Nothrow ||
|
||||||
|
NewEST == EST_DynamicNone;
|
||||||
|
if (OldNonThrowing && NewNonThrowing)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// At this point, the only remaining valid case is two matching dynamic
|
||||||
|
// specifications. We return here unless both specifications are dynamic.
|
||||||
|
if (OldEST != EST_Dynamic || NewEST != EST_Dynamic) {
|
||||||
if (MissingExceptionSpecification && Old->hasExceptionSpec() &&
|
if (MissingExceptionSpecification && Old->hasExceptionSpec() &&
|
||||||
!New->hasExceptionSpec()) {
|
!New->hasExceptionSpec()) {
|
||||||
// The old type has an exception specification of some sort, but
|
// The old type has an exception specification of some sort, but
|
||||||
// the new type does not.
|
// the new type does not.
|
||||||
*MissingExceptionSpecification = true;
|
*MissingExceptionSpecification = true;
|
||||||
|
|
||||||
if (MissingEmptyExceptionSpecification &&
|
if (MissingEmptyExceptionSpecification && OldNonThrowing) {
|
||||||
!Old->hasAnyExceptionSpec() && Old->getNumExceptions() == 0) {
|
// The old type has a throw() or noexcept(true) exception specification
|
||||||
// The old type has a throw() exception specification and the
|
// and the new type has no exception specification, and the caller asked
|
||||||
// new type has no exception specification, and the caller asked
|
|
||||||
// to handle this itself.
|
// to handle this itself.
|
||||||
*MissingEmptyExceptionSpecification = true;
|
*MissingEmptyExceptionSpecification = true;
|
||||||
}
|
}
|
||||||
|
@ -294,8 +399,11 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(OldEST == EST_Dynamic && NewEST == EST_Dynamic &&
|
||||||
|
"Exception compatibility logic error: non-dynamic spec slipped through.");
|
||||||
|
|
||||||
bool Success = true;
|
bool Success = true;
|
||||||
// Both have a definite exception spec. Collect the first set, then compare
|
// Both have a dynamic exception spec. Collect the first set, then compare
|
||||||
// to the second.
|
// to the second.
|
||||||
llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes;
|
llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes;
|
||||||
for (FunctionProtoType::exception_iterator I = Old->exception_begin(),
|
for (FunctionProtoType::exception_iterator I = Old->exception_begin(),
|
||||||
|
@ -340,19 +448,66 @@ bool Sema::CheckExceptionSpecSubset(
|
||||||
if (!SubLoc.isValid())
|
if (!SubLoc.isValid())
|
||||||
SubLoc = SuperLoc;
|
SubLoc = SuperLoc;
|
||||||
|
|
||||||
|
ExceptionSpecificationType SuperEST = Superset->getExceptionSpecType();
|
||||||
|
|
||||||
// If superset contains everything, we're done.
|
// If superset contains everything, we're done.
|
||||||
if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec())
|
if (SuperEST == EST_None || SuperEST == EST_MSAny)
|
||||||
return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
|
return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
|
||||||
|
|
||||||
|
// If there are dependent noexcept specs, assume everything is fine. Unlike
|
||||||
|
// with the equivalency check, this is safe in this case, because we don't
|
||||||
|
// want to merge declarations. Checks after instantiation will catch any
|
||||||
|
// omissions we make here.
|
||||||
|
// We also shortcut checking if a noexcept expression was bad.
|
||||||
|
|
||||||
|
FunctionProtoType::NoexceptResult SuperNR =Superset->getNoexceptSpec();
|
||||||
|
if (SuperNR == FunctionProtoType::NR_BadNoexcept ||
|
||||||
|
SuperNR == FunctionProtoType::NR_Dependent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Another case of the superset containing everything.
|
||||||
|
if (SuperNR == FunctionProtoType::NR_Throw)
|
||||||
|
return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
|
||||||
|
|
||||||
|
ExceptionSpecificationType SubEST = Subset->getExceptionSpecType();
|
||||||
|
|
||||||
// It does not. If the subset contains everything, we've failed.
|
// It does not. If the subset contains everything, we've failed.
|
||||||
if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) {
|
if (SubEST == EST_None || SubEST == EST_MSAny) {
|
||||||
Diag(SubLoc, DiagID);
|
Diag(SubLoc, DiagID);
|
||||||
if (NoteID.getDiagID() != 0)
|
if (NoteID.getDiagID() != 0)
|
||||||
Diag(SuperLoc, NoteID);
|
Diag(SuperLoc, NoteID);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Neither contains everything. Do a proper comparison.
|
FunctionProtoType::NoexceptResult SubNR = Subset->getNoexceptSpec();
|
||||||
|
if (SubNR == FunctionProtoType::NR_BadNoexcept ||
|
||||||
|
SubNR == FunctionProtoType::NR_Dependent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Another case of the subset containing everything.
|
||||||
|
if (SubNR == FunctionProtoType::NR_Throw) {
|
||||||
|
Diag(SubLoc, DiagID);
|
||||||
|
if (NoteID.getDiagID() != 0)
|
||||||
|
Diag(SuperLoc, NoteID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the subset contains nothing, we're done.
|
||||||
|
if (SubEST == EST_DynamicNone || SubNR == FunctionProtoType::NR_Nothrow)
|
||||||
|
return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
|
||||||
|
|
||||||
|
// Otherwise, if the superset contains nothing, we've failed.
|
||||||
|
if (SuperEST == EST_DynamicNone || SuperNR == FunctionProtoType::NR_Nothrow) {
|
||||||
|
Diag(SubLoc, DiagID);
|
||||||
|
if (NoteID.getDiagID() != 0)
|
||||||
|
Diag(SuperLoc, NoteID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(SuperEST == EST_Dynamic && SubEST == EST_Dynamic &&
|
||||||
|
"Exception spec subset: non-dynamic case slipped through.");
|
||||||
|
|
||||||
|
// Neither contains everything or nothing. Do a proper comparison.
|
||||||
for (FunctionProtoType::exception_iterator SubI = Subset->exception_begin(),
|
for (FunctionProtoType::exception_iterator SubI = Subset->exception_begin(),
|
||||||
SubE = Subset->exception_end(); SubI != SubE; ++SubI) {
|
SubE = Subset->exception_end(); SubI != SubE; ++SubI) {
|
||||||
// Take one type from the subset.
|
// Take one type from the subset.
|
||||||
|
|
|
@ -1501,10 +1501,12 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionProtoType::ExtProtoInfo EPI;
|
FunctionProtoType::ExtProtoInfo EPI;
|
||||||
EPI.ExceptionSpecType = EST_Dynamic;
|
|
||||||
if (HasBadAllocExceptionSpec) {
|
if (HasBadAllocExceptionSpec) {
|
||||||
|
EPI.ExceptionSpecType = EST_Dynamic;
|
||||||
EPI.NumExceptions = 1;
|
EPI.NumExceptions = 1;
|
||||||
EPI.Exceptions = &BadAllocType;
|
EPI.Exceptions = &BadAllocType;
|
||||||
|
} else {
|
||||||
|
EPI.ExceptionSpecType = EST_DynamicNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
QualType FnType = Context.getFunctionType(Return, &Argument, 1, EPI);
|
QualType FnType = Context.getFunctionType(Return, &Argument, 1, EPI);
|
||||||
|
@ -2421,7 +2423,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T,
|
||||||
FoundAssign = true;
|
FoundAssign = true;
|
||||||
const FunctionProtoType *CPT
|
const FunctionProtoType *CPT
|
||||||
= Operator->getType()->getAs<FunctionProtoType>();
|
= Operator->getType()->getAs<FunctionProtoType>();
|
||||||
if (!CPT->hasEmptyExceptionSpec()) {
|
if (!CPT->isNothrow()) {
|
||||||
AllNoThrow = false;
|
AllNoThrow = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2461,9 +2463,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T,
|
||||||
FoundConstructor = true;
|
FoundConstructor = true;
|
||||||
const FunctionProtoType *CPT
|
const FunctionProtoType *CPT
|
||||||
= Constructor->getType()->getAs<FunctionProtoType>();
|
= Constructor->getType()->getAs<FunctionProtoType>();
|
||||||
// TODO: check whether evaluating default arguments can throw.
|
// FIXME: check whether evaluating default arguments can throw.
|
||||||
// For now, we'll be conservative and assume that they can throw.
|
// For now, we'll be conservative and assume that they can throw.
|
||||||
if (!CPT->hasEmptyExceptionSpec() || CPT->getNumArgs() > 1) {
|
if (!CPT->isNothrow() || CPT->getNumArgs() > 1) {
|
||||||
AllNoThrow = false;
|
AllNoThrow = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2498,7 +2500,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T,
|
||||||
= Constructor->getType()->getAs<FunctionProtoType>();
|
= Constructor->getType()->getAs<FunctionProtoType>();
|
||||||
// TODO: check whether evaluating default arguments can throw.
|
// TODO: check whether evaluating default arguments can throw.
|
||||||
// For now, we'll be conservative and assume that they can throw.
|
// For now, we'll be conservative and assume that they can throw.
|
||||||
return CPT->hasEmptyExceptionSpec() && CPT->getNumArgs() == 0;
|
return CPT->isNothrow() && CPT->getNumArgs() == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2085,8 +2085,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
|
||||||
const FunctionProtoType *Proto = Tmpl->getType()->getAs<FunctionProtoType>();
|
const FunctionProtoType *Proto = Tmpl->getType()->getAs<FunctionProtoType>();
|
||||||
assert(Proto && "Function template without prototype?");
|
assert(Proto && "Function template without prototype?");
|
||||||
|
|
||||||
if (Proto->hasExceptionSpec() || Proto->hasAnyExceptionSpec() ||
|
if (Proto->hasExceptionSpec() || Proto->getNoReturnAttr()) {
|
||||||
Proto->getNoReturnAttr()) {
|
|
||||||
// The function has an exception specification or a "noreturn"
|
// The function has an exception specification or a "noreturn"
|
||||||
// attribute. Substitute into each of the exception types.
|
// attribute. Substitute into each of the exception types.
|
||||||
llvm::SmallVector<QualType, 4> Exceptions;
|
llvm::SmallVector<QualType, 4> Exceptions;
|
||||||
|
@ -2166,9 +2165,8 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
|
||||||
// Rebuild the function type
|
// Rebuild the function type
|
||||||
|
|
||||||
FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo();
|
FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo();
|
||||||
EPI.ExceptionSpecType = Proto->hasExceptionSpec() ?
|
// FIXME: Handle noexcept
|
||||||
(Proto->hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) :
|
EPI.ExceptionSpecType = Proto->getExceptionSpecType();
|
||||||
EST_None;
|
|
||||||
EPI.NumExceptions = Exceptions.size();
|
EPI.NumExceptions = Exceptions.size();
|
||||||
EPI.Exceptions = Exceptions.data();
|
EPI.Exceptions = Exceptions.data();
|
||||||
EPI.ExtInfo = Proto->getExtInfo();
|
EPI.ExtInfo = Proto->getExtInfo();
|
||||||
|
|
|
@ -1901,7 +1901,22 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S,
|
||||||
EPI.NumExceptions = Exceptions.size();
|
EPI.NumExceptions = Exceptions.size();
|
||||||
EPI.Exceptions = Exceptions.data();
|
EPI.Exceptions = Exceptions.data();
|
||||||
} else if (FTI.getExceptionSpecType() == EST_ComputedNoexcept) {
|
} else if (FTI.getExceptionSpecType() == EST_ComputedNoexcept) {
|
||||||
EPI.NoexceptExpr = FTI.NoexceptExpr;
|
// If an error occurred, there's no expression here.
|
||||||
|
if (Expr *NoexceptExpr = FTI.NoexceptExpr) {
|
||||||
|
assert((NoexceptExpr->isTypeDependent() ||
|
||||||
|
NoexceptExpr->getType()->getCanonicalTypeUnqualified() ==
|
||||||
|
Context.BoolTy) &&
|
||||||
|
"Parser should have made sure that the expression is boolean");
|
||||||
|
SourceLocation ErrLoc;
|
||||||
|
llvm::APSInt Dummy;
|
||||||
|
if (!NoexceptExpr->isValueDependent() &&
|
||||||
|
!NoexceptExpr->isIntegerConstantExpr(Dummy, Context, &ErrLoc,
|
||||||
|
/*evaluated*/false))
|
||||||
|
Diag(ErrLoc, diag::err_noexcept_needs_constant_expression)
|
||||||
|
<< NoexceptExpr->getSourceRange();
|
||||||
|
else
|
||||||
|
EPI.NoexceptExpr = NoexceptExpr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
T = Context.getFunctionType(T, ArgTys.data(), ArgTys.size(), EPI);
|
T = Context.getFunctionType(T, ArgTys.data(), ArgTys.size(), EPI);
|
||||||
|
|
|
@ -3117,15 +3117,18 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) {
|
||||||
EPI.Variadic = Record[Idx++];
|
EPI.Variadic = Record[Idx++];
|
||||||
EPI.TypeQuals = Record[Idx++];
|
EPI.TypeQuals = Record[Idx++];
|
||||||
EPI.RefQualifier = static_cast<RefQualifierKind>(Record[Idx++]);
|
EPI.RefQualifier = static_cast<RefQualifierKind>(Record[Idx++]);
|
||||||
bool HasExceptionSpec = Record[Idx++];
|
ExceptionSpecificationType EST =
|
||||||
bool HasAnyExceptionSpec = Record[Idx++];
|
static_cast<ExceptionSpecificationType>(Record[Idx++]);
|
||||||
EPI.ExceptionSpecType = HasExceptionSpec ?
|
EPI.ExceptionSpecType = EST;
|
||||||
(HasAnyExceptionSpec ? EST_DynamicAny : EST_Dynamic) : EST_None;
|
if (EST == EST_Dynamic) {
|
||||||
EPI.NumExceptions = Record[Idx++];
|
EPI.NumExceptions = Record[Idx++];
|
||||||
llvm::SmallVector<QualType, 2> Exceptions;
|
llvm::SmallVector<QualType, 2> Exceptions;
|
||||||
for (unsigned I = 0; I != EPI.NumExceptions; ++I)
|
for (unsigned I = 0; I != EPI.NumExceptions; ++I)
|
||||||
Exceptions.push_back(GetType(Record[Idx++]));
|
Exceptions.push_back(GetType(Record[Idx++]));
|
||||||
EPI.Exceptions = Exceptions.data();
|
EPI.Exceptions = Exceptions.data();
|
||||||
|
} else if (EST == EST_ComputedNoexcept) {
|
||||||
|
EPI.NoexceptExpr = ReadExpr(*Loc.F);
|
||||||
|
}
|
||||||
return Context->getFunctionType(ResultType, ParamTypes.data(), NumParams,
|
return Context->getFunctionType(ResultType, ParamTypes.data(), NumParams,
|
||||||
EPI);
|
EPI);
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,11 +178,14 @@ void ASTTypeWriter::VisitFunctionProtoType(const FunctionProtoType *T) {
|
||||||
Record.push_back(T->isVariadic());
|
Record.push_back(T->isVariadic());
|
||||||
Record.push_back(T->getTypeQuals());
|
Record.push_back(T->getTypeQuals());
|
||||||
Record.push_back(static_cast<unsigned>(T->getRefQualifier()));
|
Record.push_back(static_cast<unsigned>(T->getRefQualifier()));
|
||||||
Record.push_back(T->hasExceptionSpec());
|
Record.push_back(T->getExceptionSpecType());
|
||||||
Record.push_back(T->hasAnyExceptionSpec());
|
if (T->getExceptionSpecType() == EST_Dynamic) {
|
||||||
Record.push_back(T->getNumExceptions());
|
Record.push_back(T->getNumExceptions());
|
||||||
for (unsigned I = 0, N = T->getNumExceptions(); I != N; ++I)
|
for (unsigned I = 0, N = T->getNumExceptions(); I != N; ++I)
|
||||||
Writer.AddTypeRef(T->getExceptionType(I), Record);
|
Writer.AddTypeRef(T->getExceptionType(I), Record);
|
||||||
|
} else if (T->getExceptionSpecType() == EST_ComputedNoexcept) {
|
||||||
|
Writer.AddStmt(T->getNoexceptExpr());
|
||||||
|
}
|
||||||
Code = TYPE_FUNCTION_PROTO;
|
Code = TYPE_FUNCTION_PROTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
|
// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
|
||||||
|
|
||||||
// Simple parser tests, dynamic specification.
|
// Simple parser tests, dynamic specification.
|
||||||
|
|
||||||
|
@ -26,24 +26,35 @@ namespace dyn {
|
||||||
|
|
||||||
namespace noex {
|
namespace noex {
|
||||||
|
|
||||||
void f() noexcept { }
|
void f1() noexcept { }
|
||||||
void g() noexcept (true) { }
|
void f2() noexcept (true) { }
|
||||||
void h() noexcept (false) { }
|
void f3() noexcept (false) { }
|
||||||
void i() noexcept (1 < 2) { }
|
void f4() noexcept (1 < 2) { }
|
||||||
|
|
||||||
class Class {
|
class CA1 {
|
||||||
void foo() noexcept { }
|
void foo() noexcept { }
|
||||||
void bar() noexcept (true) { }
|
void bar() noexcept (true) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
void (*fptr)() noexcept;
|
void (*fptr1)() noexcept;
|
||||||
void (*gptr)() noexcept (true);
|
void (*fptr2)() noexcept (true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace bad {
|
namespace mix {
|
||||||
|
|
||||||
void f() throw(int) noexcept { } // expected-error {{cannot have both}}
|
void f() throw(int) noexcept { } // expected-error {{cannot have both}}
|
||||||
void g() noexcept throw(int) { } // expected-error {{cannot have both}}
|
void g() noexcept throw(int) { } // expected-error {{cannot have both}}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sema tests, noexcept specification
|
||||||
|
|
||||||
|
namespace noex {
|
||||||
|
|
||||||
|
struct A {};
|
||||||
|
|
||||||
|
void g1() noexcept(A()); // expected-error {{not contextually convertible}}
|
||||||
|
void g2(bool b) noexcept(b); // expected-error {{argument to noexcept specifier must be a constant expression}}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// RUN: %clang_cc1 -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
|
||||||
|
|
||||||
|
// Dynamic specifications: valid types.
|
||||||
|
|
||||||
|
struct Incomplete; // expected-note 3 {{forward declaration}}
|
||||||
|
|
||||||
|
// Exception spec must not have incomplete types, or pointers to them, except
|
||||||
|
// void.
|
||||||
|
void ic1() throw(void); // expected-error {{incomplete type 'void' is not allowed in exception specification}}
|
||||||
|
void ic2() throw(Incomplete); // expected-error {{incomplete type 'Incomplete' is not allowed in exception specification}}
|
||||||
|
void ic3() throw(void*);
|
||||||
|
void ic4() throw(Incomplete*); // expected-error {{pointer to incomplete type 'Incomplete' is not allowed in exception specification}}
|
||||||
|
void ic5() throw(Incomplete&); // expected-error {{reference to incomplete type 'Incomplete' is not allowed in exception specification}}
|
||||||
|
|
||||||
|
// Don't suppress errors in template instantiation.
|
||||||
|
template <typename T> struct TEx; // expected-note {{template is declared here}}
|
||||||
|
|
||||||
|
void tf() throw(TEx<int>); // expected-error {{implicit instantiation of undefined template}}
|
||||||
|
|
||||||
|
// DR 437, class throws itself.
|
||||||
|
struct DR437 {
|
||||||
|
void f() throw(DR437);
|
||||||
|
void g() throw(DR437*);
|
||||||
|
void h() throw(DR437&);
|
||||||
|
};
|
||||||
|
|
||||||
|
// DR 437 within a nested class
|
||||||
|
struct DR437_out {
|
||||||
|
struct DR437_in {
|
||||||
|
void f() throw(DR437_out);
|
||||||
|
void g() throw(DR437_out*);
|
||||||
|
void h() throw(DR437_out&);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,63 @@
|
||||||
|
// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
|
||||||
|
|
||||||
|
// Tests where specs are allowed and where they aren't.
|
||||||
|
|
||||||
|
namespace dyn {
|
||||||
|
|
||||||
|
// Straight from the standard:
|
||||||
|
|
||||||
|
// Plain function with spec
|
||||||
|
void f() throw(int);
|
||||||
|
|
||||||
|
// Pointer to function with spec
|
||||||
|
void (*fp)() throw (int);
|
||||||
|
|
||||||
|
// Function taking reference to function with spec
|
||||||
|
void g(void pfa() throw(int));
|
||||||
|
|
||||||
|
// Typedef for pointer to function with spec
|
||||||
|
typedef int (*pf)() throw(int); // expected-error {{specifications are not allowed in typedefs}}
|
||||||
|
|
||||||
|
// Some more:
|
||||||
|
|
||||||
|
// Function returning function with spec
|
||||||
|
void (*h())() throw(int);
|
||||||
|
|
||||||
|
// Ultimate parser thrill: function with spec returning function with spec and
|
||||||
|
// taking pointer to function with spec.
|
||||||
|
// The actual function throws int, the return type double, the argument float.
|
||||||
|
void (*i() throw(int))(void (*)() throw(float)) throw(double);
|
||||||
|
|
||||||
|
// Pointer to pointer to function taking function with spec
|
||||||
|
void (**k)(void pfa() throw(int)); // no-error
|
||||||
|
|
||||||
|
// Pointer to pointer to function with spec
|
||||||
|
void (**j)() throw(int); // expected-error {{not allowed beyond a single}}
|
||||||
|
|
||||||
|
// Pointer to function returning pointer to pointer to function with spec
|
||||||
|
void (**(*h())())() throw(int); // expected-error {{not allowed beyond a single}}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace noex {
|
||||||
|
|
||||||
|
// These parallel those from above.
|
||||||
|
|
||||||
|
void f() noexcept(false);
|
||||||
|
|
||||||
|
void (*fp)() noexcept(false);
|
||||||
|
|
||||||
|
void g(void pfa() noexcept(false));
|
||||||
|
|
||||||
|
typedef int (*pf)() noexcept(false); // expected-error {{specifications are not allowed in typedefs}}
|
||||||
|
|
||||||
|
void (*h())() noexcept(false);
|
||||||
|
|
||||||
|
void (*i() noexcept(false))(void (*)() noexcept(true)) noexcept(false);
|
||||||
|
|
||||||
|
void (**k)(void pfa() noexcept(false)); // no-error
|
||||||
|
|
||||||
|
void (**j)() noexcept(false); // expected-error {{not allowed beyond a single}}
|
||||||
|
|
||||||
|
void (**(*h())())() noexcept(false); // expected-error {{not allowed beyond a single}}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
|
||||||
|
|
||||||
|
// Exception specification compatibility.
|
||||||
|
// We test function pointers, because functions have an extra rule in p4.
|
||||||
|
|
||||||
|
// Same type is compatible
|
||||||
|
extern void (*r1)() throw(int);
|
||||||
|
extern void (*r1)() throw(int);
|
||||||
|
|
||||||
|
// Typedefs don't matter.
|
||||||
|
typedef int INT;
|
||||||
|
extern void (*r2)() throw(int);
|
||||||
|
extern void (*r2)() throw(INT);
|
||||||
|
|
||||||
|
// Order doesn't matter.
|
||||||
|
extern void (*r3)() throw(int, float);
|
||||||
|
extern void (*r3)() throw(float, int);
|
||||||
|
|
||||||
|
// MS throw-any spec and no spec at all are compatible
|
||||||
|
extern void (*r4)();
|
||||||
|
extern void (*r4)() throw(...);
|
||||||
|
|
||||||
|
// throw(X) and no spec are not compatible
|
||||||
|
extern void (*r5)() throw(int); // expected-note {{previous declaration}}
|
||||||
|
extern void (*r5)(); // expected-error {{exception specification in declaration does not match}}
|
||||||
|
|
||||||
|
// For functions, we accept this with a warning.
|
||||||
|
extern void f5() throw(int); // expected-note {{previous declaration}}
|
||||||
|
extern void f5(); // expected-warning {{missing exception specification}}
|
||||||
|
|
||||||
|
// Different types are not compatible.
|
||||||
|
extern void (*r7)() throw(int); // expected-note {{previous declaration}}
|
||||||
|
extern void (*r7)() throw(float); // expected-error {{exception specification in declaration does not match}}
|
||||||
|
|
||||||
|
// Top-level const doesn't matter.
|
||||||
|
extern void (*r8)() throw(int);
|
||||||
|
extern void (*r8)() throw(const int);
|
||||||
|
|
||||||
|
// Multiple appearances don't matter.
|
||||||
|
extern void (*r9)() throw(int, int);
|
||||||
|
extern void (*r9)() throw(int, int);
|
||||||
|
|
||||||
|
|
||||||
|
// noexcept is compatible with itself
|
||||||
|
extern void (*r10)() noexcept;
|
||||||
|
extern void (*r10)() noexcept;
|
||||||
|
|
||||||
|
// noexcept(true) is compatible with noexcept
|
||||||
|
extern void (*r11)() noexcept;
|
||||||
|
extern void (*r11)() noexcept(true);
|
||||||
|
|
||||||
|
// noexcept(false) isn't
|
||||||
|
extern void (*r12)() noexcept; // expected-note {{previous declaration}}
|
||||||
|
extern void (*r12)() noexcept(false); // expected-error {{does not match}}
|
||||||
|
|
||||||
|
// The form of the boolean expression doesn't matter.
|
||||||
|
extern void (*r13)() noexcept(1 < 2);
|
||||||
|
extern void (*r13)() noexcept(2 > 1);
|
||||||
|
|
||||||
|
// noexcept(false) is incompatible with noexcept(true)
|
||||||
|
extern void (*r14)() noexcept(true); // expected-note {{previous declaration}}
|
||||||
|
extern void (*r14)() noexcept(false); // expected-error {{does not match}}
|
||||||
|
|
||||||
|
// noexcept(false) is compatible with itself
|
||||||
|
extern void (*r15)() noexcept(false);
|
||||||
|
extern void (*r15)() noexcept(false);
|
||||||
|
|
||||||
|
// noexcept(false) is compatible with MS throw(...)
|
||||||
|
extern void (*r16)() noexcept(false);
|
||||||
|
extern void (*r16)() throw(...);
|
||||||
|
|
||||||
|
// noexcept(false) is *not* compatible with no spec
|
||||||
|
extern void (*r17)(); // expected-note {{previous declaration}}
|
||||||
|
extern void (*r17)() noexcept(false); // expected-error {{does not match}}
|
||||||
|
|
||||||
|
// except for functions
|
||||||
|
void f17();
|
||||||
|
void f17() noexcept(false);
|
||||||
|
|
||||||
|
// noexcept(false) is compatible with dynamic specs that throw unless
|
||||||
|
// CWG 1073 resolution is accepted. Clang implements it.
|
||||||
|
//extern void (*r18)() throw(int);
|
||||||
|
//extern void (*r18)() noexcept(false);
|
||||||
|
|
||||||
|
// noexcept(true) is compatible with dynamic specs that don't throw
|
||||||
|
extern void (*r19)() throw();
|
||||||
|
extern void (*r19)() noexcept(true);
|
||||||
|
|
||||||
|
// The other way round doesn't work.
|
||||||
|
extern void (*r20)() throw(); // expected-note {{previous declaration}}
|
||||||
|
extern void (*r20)() noexcept(false); // expected-error {{does not match}}
|
||||||
|
|
||||||
|
extern void (*r21)() throw(int); // expected-note {{previous declaration}}
|
||||||
|
extern void (*r21)() noexcept(true); // expected-error {{does not match}}
|
|
@ -0,0 +1,85 @@
|
||||||
|
// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
|
||||||
|
|
||||||
|
// Assignment of function pointers.
|
||||||
|
|
||||||
|
struct A
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B1 : A
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B2 : A
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct D : B1, B2
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct P : private A
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
// Some functions to play with below.
|
||||||
|
void s1() throw();
|
||||||
|
void s2() throw(int);
|
||||||
|
void s3() throw(A);
|
||||||
|
void s4() throw(B1);
|
||||||
|
void s5() throw(D);
|
||||||
|
void s6();
|
||||||
|
void s7() throw(int, float);
|
||||||
|
void (*s8())() throw(B1); // s8 returns a pointer to function with spec
|
||||||
|
void s9(void (*)() throw(B1)); // s9 takes pointer to function with spec
|
||||||
|
|
||||||
|
void s10() noexcept;
|
||||||
|
void s11() noexcept(true);
|
||||||
|
void s12() noexcept(false);
|
||||||
|
|
||||||
|
void fnptrs()
|
||||||
|
{
|
||||||
|
// Assignment and initialization of function pointers.
|
||||||
|
void (*t1)() throw() = &s1; // valid
|
||||||
|
t1 = &s2; // expected-error {{not superset}} expected-error {{incompatible type}}
|
||||||
|
t1 = &s3; // expected-error {{not superset}} expected-error {{incompatible type}}
|
||||||
|
void (&t2)() throw() = s2; // expected-error {{not superset}}
|
||||||
|
void (*t3)() throw(int) = &s2; // valid
|
||||||
|
void (*t4)() throw(A) = &s1; // valid
|
||||||
|
t4 = &s3; // valid
|
||||||
|
t4 = &s4; // valid
|
||||||
|
t4 = &s5; // expected-error {{not superset}} expected-error {{incompatible type}}
|
||||||
|
void (*t5)() = &s1; // valid
|
||||||
|
t5 = &s2; // valid
|
||||||
|
t5 = &s6; // valid
|
||||||
|
t5 = &s7; // valid
|
||||||
|
t1 = t3; // expected-error {{not superset}} expected-error {{incompatible type}}
|
||||||
|
t3 = t1; // valid
|
||||||
|
void (*t6)() throw(B1);
|
||||||
|
t6 = t4; // expected-error {{not superset}} expected-error {{incompatible type}}
|
||||||
|
t4 = t6; // valid
|
||||||
|
t5 = t1; // valid
|
||||||
|
t1 = t5; // expected-error {{not superset}} expected-error {{incompatible type}}
|
||||||
|
|
||||||
|
// return types and arguments must match exactly, no inheritance allowed
|
||||||
|
void (*(*t7)())() throw(B1) = &s8; // valid
|
||||||
|
void (*(*t8)())() throw(A) = &s8; // expected-error {{return types differ}}
|
||||||
|
void (*(*t9)())() throw(D) = &s8; // expected-error {{return types differ}}
|
||||||
|
void (*t10)(void (*)() throw(B1)) = &s9; // valid expected-warning{{disambiguated}}
|
||||||
|
void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}}
|
||||||
|
void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Member function stuff
|
||||||
|
|
||||||
|
struct Str1 { void f() throw(int); }; // expected-note {{previous declaration}}
|
||||||
|
void Str1::f() // expected-warning {{missing exception specification}}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void mfnptr()
|
||||||
|
{
|
||||||
|
void (Str1::*pfn1)() throw(int) = &Str1::f; // valid
|
||||||
|
void (Str1::*pfn2)() = &Str1::f; // valid
|
||||||
|
void (Str1::*pfn3)() throw() = &Str1::f; // expected-error {{not superset}}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
|
||||||
|
|
||||||
|
// Compatibility of virtual functions.
|
||||||
|
|
||||||
|
struct A
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B1 : A
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B2 : A
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct D : B1, B2
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct P : private A
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Base
|
||||||
|
{
|
||||||
|
virtual void f1() throw();
|
||||||
|
virtual void f2() throw(int, float);
|
||||||
|
|
||||||
|
virtual void f3() throw(int, float);
|
||||||
|
virtual void f4() throw(A);
|
||||||
|
virtual void f5() throw(A, int, float);
|
||||||
|
virtual void f6();
|
||||||
|
|
||||||
|
virtual void f7() noexcept;
|
||||||
|
virtual void f8() noexcept;
|
||||||
|
virtual void f9() noexcept(false);
|
||||||
|
virtual void f10() noexcept(false);
|
||||||
|
|
||||||
|
virtual void f11() throw();
|
||||||
|
virtual void f12() noexcept;
|
||||||
|
virtual void f13() noexcept(false);
|
||||||
|
virtual void f14() throw(int);
|
||||||
|
|
||||||
|
virtual void f15();
|
||||||
|
virtual void f16();
|
||||||
|
|
||||||
|
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}}
|
||||||
|
virtual void g5() throw(A); // expected-note {{overridden virtual function is here}}
|
||||||
|
|
||||||
|
virtual void g6() noexcept; // expected-note {{overridden virtual function is here}}
|
||||||
|
virtual void g7() noexcept; // expected-note {{overridden virtual function is here}}
|
||||||
|
|
||||||
|
virtual void g8() noexcept; // expected-note {{overridden virtual function is here}}
|
||||||
|
virtual void g9() throw(); // expected-note {{overridden virtual function is here}}
|
||||||
|
virtual void g10() throw(int); // expected-note {{overridden virtual function is here}}
|
||||||
|
};
|
||||||
|
struct Derived : Base
|
||||||
|
{
|
||||||
|
virtual void f1() throw();
|
||||||
|
virtual void f2() throw(float, int);
|
||||||
|
|
||||||
|
virtual void f3() throw(float);
|
||||||
|
virtual void f4() throw(B1);
|
||||||
|
virtual void f5() throw(B1, B2, int);
|
||||||
|
virtual void f6() throw(B2, B2, int, float, char, double, bool);
|
||||||
|
|
||||||
|
virtual void f7() noexcept;
|
||||||
|
virtual void f8() noexcept(true);
|
||||||
|
virtual void f9() noexcept(true);
|
||||||
|
virtual void f10() noexcept(false);
|
||||||
|
|
||||||
|
virtual void f11() noexcept;
|
||||||
|
virtual void f12() throw();
|
||||||
|
virtual void f13() throw(int);
|
||||||
|
virtual void f14() noexcept(true);
|
||||||
|
|
||||||
|
virtual void f15() noexcept;
|
||||||
|
virtual void f16() throw();
|
||||||
|
|
||||||
|
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}}
|
||||||
|
virtual void g5() throw(P); // expected-error {{exception specification of overriding function is more lax}}
|
||||||
|
|
||||||
|
virtual void g6() noexcept(false); // expected-error {{exception specification of overriding function is more lax}}
|
||||||
|
virtual void g7(); // expected-error {{exception specification of overriding function is more lax}}
|
||||||
|
|
||||||
|
virtual void g8() throw(int); // expected-error {{exception specification of overriding function is more lax}}
|
||||||
|
virtual void g9() noexcept(false); // expected-error {{exception specification of overriding function is more lax}}
|
||||||
|
virtual void g10() noexcept(false); // expected-error {{exception specification of overriding function is more lax}}
|
||||||
|
};
|
|
@ -1,193 +0,0 @@
|
||||||
// RUN: %clang_cc1 -fsyntax-only -verify -fexceptions -fcxx-exceptions %s
|
|
||||||
|
|
||||||
// Straight from the standard:
|
|
||||||
// Plain function with spec
|
|
||||||
void f() throw(int);
|
|
||||||
// Pointer to function with spec
|
|
||||||
void (*fp)() throw (int);
|
|
||||||
// Function taking reference to function with spec
|
|
||||||
void g(void pfa() throw(int));
|
|
||||||
// Typedef for pointer to function with spec
|
|
||||||
typedef int (*pf)() throw(int); // expected-error {{specifications are not allowed in typedefs}}
|
|
||||||
|
|
||||||
// Some more:
|
|
||||||
// Function returning function with spec
|
|
||||||
void (*h())() throw(int);
|
|
||||||
// Ultimate parser thrill: function with spec returning function with spec and
|
|
||||||
// taking pointer to function with spec.
|
|
||||||
// The actual function throws int, the return type double, the argument float.
|
|
||||||
void (*i() throw(int))(void (*)() throw(float)) throw(double);
|
|
||||||
// Pointer to pointer to function taking function with spec
|
|
||||||
void (**k)(void pfa() throw(int)); // no-error
|
|
||||||
// Pointer to pointer to function with spec
|
|
||||||
void (**j)() throw(int); // expected-error {{not allowed beyond a single}}
|
|
||||||
// Pointer to function returning pointer to pointer to function with spec
|
|
||||||
void (**(*h())())() throw(int); // expected-error {{not allowed beyond a single}}
|
|
||||||
|
|
||||||
struct Incomplete; // expected-note 3 {{forward declaration}}
|
|
||||||
|
|
||||||
// Exception spec must not have incomplete types, or pointers to them, except
|
|
||||||
// void.
|
|
||||||
void ic1() throw(void); // expected-error {{incomplete type 'void' is not allowed in exception specification}}
|
|
||||||
void ic2() throw(Incomplete); // expected-error {{incomplete type 'Incomplete' is not allowed in exception specification}}
|
|
||||||
void ic3() throw(void*);
|
|
||||||
void ic4() throw(Incomplete*); // expected-error {{pointer to incomplete type 'Incomplete' is not allowed in exception specification}}
|
|
||||||
void ic5() throw(Incomplete&); // expected-error {{reference to incomplete type '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 r4() throw(int, float);
|
|
||||||
void r4() throw(float, int);
|
|
||||||
|
|
||||||
void r5() throw(int); // expected-note {{previous declaration}}
|
|
||||||
void r5(); // expected-warning {{missing exception specification}}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Multiple appearances don't matter.
|
|
||||||
void r9() throw(int, int);
|
|
||||||
void r9() throw(int, int);
|
|
||||||
|
|
||||||
struct A
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
struct B1 : A
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
struct B2 : A
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
struct D : B1, B2
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
struct P : private A
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Base
|
|
||||||
{
|
|
||||||
virtual void f1() 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}}
|
|
||||||
virtual void g5() throw(A); // expected-note {{overridden virtual function is here}}
|
|
||||||
};
|
|
||||||
struct Derived : Base
|
|
||||||
{
|
|
||||||
virtual void f1() throw();
|
|
||||||
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}}
|
|
||||||
virtual void g5() throw(P); // expected-error {{exception specification of overriding function is more lax}}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Some functions to play with below.
|
|
||||||
void s1() throw();
|
|
||||||
void s2() throw(int);
|
|
||||||
void s3() throw(A);
|
|
||||||
void s4() throw(B1);
|
|
||||||
void s5() throw(D);
|
|
||||||
void s6();
|
|
||||||
void s7() throw(int, float);
|
|
||||||
void (*s8())() throw(B1); // s8 returns a pointer to function with spec
|
|
||||||
void s9(void (*)() throw(B1)); // s9 takes pointer to function with spec
|
|
||||||
|
|
||||||
void fnptrs()
|
|
||||||
{
|
|
||||||
// Assignment and initialization of function pointers.
|
|
||||||
void (*t1)() throw() = &s1; // valid
|
|
||||||
t1 = &s2; // expected-error {{not superset}} expected-error {{incompatible type}}
|
|
||||||
t1 = &s3; // expected-error {{not superset}} expected-error {{incompatible type}}
|
|
||||||
void (&t2)() throw() = s2; // expected-error {{not superset}}
|
|
||||||
void (*t3)() throw(int) = &s2; // valid
|
|
||||||
void (*t4)() throw(A) = &s1; // valid
|
|
||||||
t4 = &s3; // valid
|
|
||||||
t4 = &s4; // valid
|
|
||||||
t4 = &s5; // expected-error {{not superset}} expected-error {{incompatible type}}
|
|
||||||
void (*t5)() = &s1; // valid
|
|
||||||
t5 = &s2; // valid
|
|
||||||
t5 = &s6; // valid
|
|
||||||
t5 = &s7; // valid
|
|
||||||
t1 = t3; // expected-error {{not superset}} expected-error {{incompatible type}}
|
|
||||||
t3 = t1; // valid
|
|
||||||
void (*t6)() throw(B1);
|
|
||||||
t6 = t4; // expected-error {{not superset}} expected-error {{incompatible type}}
|
|
||||||
t4 = t6; // valid
|
|
||||||
t5 = t1; // valid
|
|
||||||
t1 = t5; // expected-error {{not superset}} expected-error {{incompatible type}}
|
|
||||||
|
|
||||||
// return types and arguments must match exactly, no inheritance allowed
|
|
||||||
void (*(*t7)())() throw(B1) = &s8; // valid
|
|
||||||
void (*(*t8)())() throw(A) = &s8; // expected-error {{return types differ}}
|
|
||||||
void (*(*t9)())() throw(D) = &s8; // expected-error {{return types differ}}
|
|
||||||
void (*t10)(void (*)() throw(B1)) = &s9; // valid expected-warning{{disambiguated}}
|
|
||||||
void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}}
|
|
||||||
void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Member function stuff
|
|
||||||
|
|
||||||
struct Str1 { void f() throw(int); }; // expected-note {{previous declaration}}
|
|
||||||
void Str1::f() // expected-warning {{missing exception specification}}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void mfnptr()
|
|
||||||
{
|
|
||||||
void (Str1::*pfn1)() throw(int) = &Str1::f; // valid
|
|
||||||
void (Str1::*pfn2)() = &Str1::f; // valid
|
|
||||||
void (Str1::*pfn3)() throw() = &Str1::f; // expected-error {{not superset}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't suppress errors in template instantiation.
|
|
||||||
template <typename T> struct TEx; // expected-note {{template is declared here}}
|
|
||||||
|
|
||||||
void tf() throw(TEx<int>); // expected-error {{implicit instantiation of undefined template}}
|
|
||||||
|
|
||||||
// DR 437, class throws itself.
|
|
||||||
struct DR437 {
|
|
||||||
void f() throw(DR437);
|
|
||||||
void g() throw(DR437*);
|
|
||||||
void h() throw(DR437&);
|
|
||||||
};
|
|
||||||
|
|
||||||
// DR 437 within a nested class
|
|
||||||
struct DR437_out {
|
|
||||||
struct DR437_in {
|
|
||||||
void f() throw(DR437_out);
|
|
||||||
void g() throw(DR437_out*);
|
|
||||||
void h() throw(DR437_out&);
|
|
||||||
};
|
|
||||||
};
|
|
Loading…
Reference in New Issue