PR21437, final part of DR1330: delay-parsing of exception-specifications. This

is a re-commit of Doug's r154844 (modernized and updated to fit into current
Clang).

llvm-svn: 221918
This commit is contained in:
Richard Smith 2014-11-13 20:01:57 +00:00
parent cc8d3b8774
commit 0b3a46247e
28 changed files with 404 additions and 78 deletions

View File

@ -930,6 +930,12 @@ public:
/// \brief Change the result type of a function type once it is deduced.
void adjustDeducedFunctionResultType(FunctionDecl *FD, QualType ResultType);
/// \brief Change the exception specification on a function once it is
/// delay-parsed, instantiated, or computed.
void adjustExceptionSpec(FunctionDecl *FD,
const FunctionProtoType::ExceptionSpecInfo &ESI,
bool AsWritten = false);
/// \brief Return the uniqued reference to the type for a complex
/// number with the specified element type.
QualType getComplexType(QualType T) const;

View File

@ -68,6 +68,9 @@ public:
/// \brief Return the TypeLoc wrapper for the type source info.
TypeLoc getTypeLoc() const; // implemented in TypeLoc.h
/// \brief Override the type stored in this TypeSourceInfo. Use with caution!
void overrideType(QualType T) { Ty = T; }
};
/// TranslationUnitDecl - The top declaration context.

View File

@ -1245,6 +1245,7 @@ protected:
class FunctionTypeBitfields {
friend class FunctionType;
friend class FunctionProtoType;
unsigned : NumTypeBits;
@ -1259,6 +1260,11 @@ protected:
/// C++ 8.3.5p4: The return type, the parameter type list and the
/// cv-qualifier-seq, [...], are part of the function type.
unsigned TypeQuals : 3;
/// \brief The ref-qualifier associated with a \c FunctionProtoType.
///
/// This is a value of type \c RefQualifierKind.
unsigned RefQualifier : 2;
};
class ObjCObjectTypeBitfields {
@ -2765,7 +2771,7 @@ class FunctionType : public Type {
protected:
FunctionType(TypeClass tc, QualType res,
unsigned typeQuals, QualType Canonical, bool Dependent,
QualType Canonical, bool Dependent,
bool InstantiationDependent,
bool VariablyModified, bool ContainsUnexpandedParameterPack,
ExtInfo Info)
@ -2773,7 +2779,6 @@ protected:
ContainsUnexpandedParameterPack),
ResultType(res) {
FunctionTypeBits.ExtInfo = Info.Bits;
FunctionTypeBits.TypeQuals = typeQuals;
}
unsigned getTypeQuals() const { return FunctionTypeBits.TypeQuals; }
@ -2810,7 +2815,7 @@ public:
/// no information available about its arguments.
class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode {
FunctionNoProtoType(QualType Result, QualType Canonical, ExtInfo Info)
: FunctionType(FunctionNoProto, Result, 0, Canonical,
: FunctionType(FunctionNoProto, Result, Canonical,
/*Dependent=*/false, /*InstantiationDependent=*/false,
Result->isVariablyModifiedType(),
/*ContainsUnexpandedParameterPack=*/false, Info) {}
@ -2914,7 +2919,7 @@ private:
unsigned NumExceptions : 9;
/// ExceptionSpecType - The type of exception specification this function has.
unsigned ExceptionSpecType : 3;
unsigned ExceptionSpecType : 4;
/// HasAnyConsumedParams - Whether this function has any consumed parameters.
unsigned HasAnyConsumedParams : 1;
@ -2925,11 +2930,6 @@ private:
/// HasTrailingReturn - Whether this function has a trailing return type.
unsigned HasTrailingReturn : 1;
/// \brief The ref-qualifier associated with a \c FunctionProtoType.
///
/// This is a value of type \c RefQualifierKind.
unsigned RefQualifier : 2;
// ParamInfo - There is an variable size array after the class in memory that
// holds the parameter types.
@ -3076,7 +3076,7 @@ public:
/// \brief Retrieve the ref-qualifier associated with this function type.
RefQualifierKind getRefQualifier() const {
return static_cast<RefQualifierKind>(RefQualifier);
return static_cast<RefQualifierKind>(FunctionTypeBits.RefQualifier);
}
typedef const QualType *param_type_iterator;

View File

@ -496,6 +496,8 @@ def ext_ellipsis_exception_spec : Extension<
InGroup<Microsoft>;
def err_dynamic_and_noexcept_specification : Error<
"cannot have both throw() and noexcept() clause on the same function">;
def err_except_spec_unparsed : Error<
"unexpected end of exception specification">;
def warn_cxx98_compat_noexcept_decl : Warning<
"noexcept specifications are incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;

View File

@ -1117,6 +1117,8 @@ def warn_missing_exception_specification : Warning<
"%0 is missing exception specification '%1'">;
def err_noexcept_needs_constant_expression : Error<
"argument to noexcept specifier must be a constant expression">;
def err_exception_spec_not_parsed : Error<
"exception specification is not available until end of class definition">;
// C++ access checking
def err_class_redeclared_with_different_access : Error<

View File

@ -26,7 +26,8 @@ enum ExceptionSpecificationType {
EST_BasicNoexcept, ///< noexcept
EST_ComputedNoexcept, ///< noexcept(expression)
EST_Unevaluated, ///< not evaluated yet, for special member function
EST_Uninstantiated ///< not instantiated yet
EST_Uninstantiated, ///< not instantiated yet
EST_Unparsed ///< not parsed yet
};
inline bool isDynamicExceptionSpec(ExceptionSpecificationType ESpecType) {

View File

@ -117,6 +117,7 @@ TOK(eod) // End of preprocessing directive (end of line inside a
// directive).
TOK(code_completion) // Code completion marker
TOK(cxx_defaultarg_end) // C++ default argument end marker
TOK(cxx_exceptspec_end) // C++ exception-specification end marker
// C99 6.4.9: Comments.
TOK(comment) // Comment (only in -E -C[C] mode)

View File

@ -1453,10 +1453,12 @@ private:
ExprResult ParseThrowExpression();
ExceptionSpecificationType tryParseExceptionSpecification(
bool Delayed,
SourceRange &SpecificationRange,
SmallVectorImpl<ParsedType> &DynamicExceptions,
SmallVectorImpl<SourceRange> &DynamicExceptionRanges,
ExprResult &NoexceptExpr);
ExprResult &NoexceptExpr,
CachedTokens *&ExceptionSpecTokens);
// EndLoc is filled with the location of the last token of the specification.
ExceptionSpecificationType ParseDynamicExceptionSpecification(

View File

@ -1176,7 +1176,7 @@ struct DeclaratorChunk {
unsigned TypeQuals : 3;
/// ExceptionSpecType - An ExceptionSpecificationType value.
unsigned ExceptionSpecType : 3;
unsigned ExceptionSpecType : 4;
/// DeleteParams - If this is true, we need to delete[] Params.
unsigned DeleteParams : 1;
@ -1243,6 +1243,10 @@ struct DeclaratorChunk {
/// \brief Pointer to the expression in the noexcept-specifier of this
/// function, if it has one.
Expr *NoexceptExpr;
/// \brief Pointer to the cached tokens for an exception-specification
/// that has not yet been parsed.
CachedTokens *ExceptionSpecTokens;
};
/// \brief If HasTrailingReturnType is true, this is the trailing return
@ -1269,6 +1273,8 @@ struct DeclaratorChunk {
delete[] Params;
if (getExceptionSpecType() == EST_Dynamic)
delete[] Exceptions;
else if (getExceptionSpecType() == EST_Unparsed)
delete ExceptionSpecTokens;
}
/// isKNRPrototype - Return true if this is a K&R style identifier list,
@ -1464,6 +1470,7 @@ struct DeclaratorChunk {
SourceRange *ExceptionRanges,
unsigned NumExceptions,
Expr *NoexceptExpr,
CachedTokens *ExceptionSpecTokens,
SourceLocation LocalRangeBegin,
SourceLocation LocalRangeEnd,
Declarator &TheDeclarator,

View File

@ -4106,6 +4106,16 @@ public:
SmallVectorImpl<QualType> &Exceptions,
FunctionProtoType::ExceptionSpecInfo &ESI);
/// \brief Add an exception-specification to the given member function
/// (or member function template). The exception-specification was parsed
/// after the method itself was declared.
void actOnDelayedExceptionSpecification(Decl *Method,
ExceptionSpecificationType EST,
SourceRange SpecificationRange,
ArrayRef<ParsedType> DynamicExceptions,
ArrayRef<SourceRange> DynamicExceptionRanges,
Expr *NoexceptExpr);
/// \brief Determine if a special member function should have a deleted
/// definition when it is defaulted.
bool ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM,

View File

@ -2111,6 +2111,62 @@ void ASTContext::adjustDeducedFunctionResultType(FunctionDecl *FD,
L->DeducedReturnType(FD, ResultType);
}
/// Get a function type and produce the equivalent function type with the
/// specified exception specification. Type sugar that can be present on a
/// declaration of a function with an exception specification is permitted
/// and preserved. Other type sugar (for instance, typedefs) is not.
static QualType getFunctionTypeWithExceptionSpec(
ASTContext &Context, QualType Orig,
const FunctionProtoType::ExceptionSpecInfo &ESI) {
// Might have some parens.
if (auto *PT = dyn_cast<ParenType>(Orig))
return Context.getParenType(
getFunctionTypeWithExceptionSpec(Context, PT->getInnerType(), ESI));
// Might have a calling-convention attribute.
if (auto *AT = dyn_cast<AttributedType>(Orig))
return Context.getAttributedType(
AT->getAttrKind(),
getFunctionTypeWithExceptionSpec(Context, AT->getModifiedType(), ESI),
getFunctionTypeWithExceptionSpec(Context, AT->getEquivalentType(),
ESI));
// Anything else must be a function type. Rebuild it with the new exception
// specification.
const FunctionProtoType *Proto = cast<FunctionProtoType>(Orig);
return Context.getFunctionType(
Proto->getReturnType(), Proto->getParamTypes(),
Proto->getExtProtoInfo().withExceptionSpec(ESI));
}
void ASTContext::adjustExceptionSpec(
FunctionDecl *FD, const FunctionProtoType::ExceptionSpecInfo &ESI,
bool AsWritten) {
// Update the type.
QualType Updated =
getFunctionTypeWithExceptionSpec(*this, FD->getType(), ESI);
FD->setType(Updated);
if (!AsWritten)
return;
// Update the type in the type source information too.
if (TypeSourceInfo *TSInfo = FD->getTypeSourceInfo()) {
// If the type and the type-as-written differ, we may need to update
// the type-as-written too.
if (TSInfo->getType() != FD->getType())
Updated = getFunctionTypeWithExceptionSpec(*this, TSInfo->getType(), ESI);
// FIXME: When we get proper type location information for exceptions,
// we'll also have to rebuild the TypeSourceInfo. For now, we just patch
// up the TypeSourceInfo;
assert(TypeLoc::getFullDataSizeForType(Updated) ==
TypeLoc::getFullDataSizeForType(TSInfo->getType()) &&
"TypeLoc size mismatch from updating exception specification");
TSInfo->overrideType(Updated);
}
}
/// getComplexType - Return the uniqued reference to the type for a complex
/// number with the specified element type.
QualType ASTContext::getComplexType(QualType T) const {

View File

@ -1593,7 +1593,7 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) {
FunctionProtoType::FunctionProtoType(QualType result, ArrayRef<QualType> params,
QualType canonical,
const ExtProtoInfo &epi)
: FunctionType(FunctionProto, result, epi.TypeQuals, canonical,
: FunctionType(FunctionProto, result, canonical,
result->isDependentType(),
result->isInstantiationDependentType(),
result->isVariablyModifiedType(),
@ -1602,10 +1602,12 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef<QualType> params,
NumExceptions(epi.ExceptionSpec.Exceptions.size()),
ExceptionSpecType(epi.ExceptionSpec.Type),
HasAnyConsumedParams(epi.ConsumedParameters != nullptr),
Variadic(epi.Variadic), HasTrailingReturn(epi.HasTrailingReturn),
RefQualifier(epi.RefQualifier) {
Variadic(epi.Variadic), HasTrailingReturn(epi.HasTrailingReturn) {
assert(NumParams == params.size() && "function has too many parameters");
FunctionTypeBits.TypeQuals = epi.TypeQuals;
FunctionTypeBits.RefQualifier = epi.RefQualifier;
// Fill in the trailing argument array.
QualType *argSlot = reinterpret_cast<QualType*>(this+1);
for (unsigned i = 0; i != NumParams; ++i) {
@ -1772,7 +1774,7 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
assert(!(unsigned(epi.Variadic) & ~1) &&
!(unsigned(epi.TypeQuals) & ~255) &&
!(unsigned(epi.RefQualifier) & ~3) &&
!(unsigned(epi.ExceptionSpec.Type) & ~7) &&
!(unsigned(epi.ExceptionSpec.Type) & ~15) &&
"Values larger than expected.");
ID.AddInteger(unsigned(epi.Variadic) +
(epi.TypeQuals << 1) +

View File

@ -366,6 +366,76 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
}
}
// Parse a delayed exception-specification, if there is one.
if (CachedTokens *Toks = LM.ExceptionSpecTokens) {
// Save the current token position.
SourceLocation origLoc = Tok.getLocation();
// Parse the default argument from its saved token stream.
Toks->push_back(Tok); // So that the current token doesn't get lost
PP.EnterTokenStream(&Toks->front(), Toks->size(), true, false);
// Consume the previously-pushed token.
ConsumeAnyToken();
// C++11 [expr.prim.general]p3:
// If a declaration declares a member function or member function
// template of a class X, the expression this is a prvalue of type
// "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
// and the end of the function-definition, member-declarator, or
// declarator.
CXXMethodDecl *Method;
if (FunctionTemplateDecl *FunTmpl
= dyn_cast<FunctionTemplateDecl>(LM.Method))
Method = cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl());
else
Method = cast<CXXMethodDecl>(LM.Method);
Sema::CXXThisScopeRAII ThisScope(Actions, Method->getParent(),
Method->getTypeQualifiers(),
getLangOpts().CPlusPlus11);
// Parse the exception-specification.
SourceRange SpecificationRange;
SmallVector<ParsedType, 4> DynamicExceptions;
SmallVector<SourceRange, 4> DynamicExceptionRanges;
ExprResult NoexceptExpr;
CachedTokens *ExceptionSpecTokens;
ExceptionSpecificationType EST
= tryParseExceptionSpecification(/*Delayed=*/false, SpecificationRange,
DynamicExceptions,
DynamicExceptionRanges, NoexceptExpr,
ExceptionSpecTokens);
// Clean up the remaining tokens.
if (Tok.is(tok::cxx_exceptspec_end))
ConsumeToken();
else if (EST != EST_None)
Diag(Tok.getLocation(), diag::err_except_spec_unparsed);
// Attach the exception-specification to the method.
if (EST != EST_None)
Actions.actOnDelayedExceptionSpecification(LM.Method, EST,
SpecificationRange,
DynamicExceptions,
DynamicExceptionRanges,
NoexceptExpr.isUsable()?
NoexceptExpr.get() : nullptr);
assert(!PP.getSourceManager().isBeforeInTranslationUnit(origLoc,
Tok.getLocation()) &&
"tryParseExceptionSpecification went over the exception tokens!");
// There could be leftover tokens (e.g. because of an error).
// Skip through until we reach the original token position.
while (Tok.getLocation() != origLoc && Tok.isNot(tok::eof))
ConsumeAnyToken();
delete Toks;
LM.ExceptionSpecTokens = nullptr;
}
PrototypeScope.Exit();
// Finish the delayed C++ method declaration.

View File

@ -5193,6 +5193,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
SmallVector<ParsedType, 2> DynamicExceptions;
SmallVector<SourceRange, 2> DynamicExceptionRanges;
ExprResult NoexceptExpr;
CachedTokens *ExceptionSpecTokens = 0;
ParsedAttributes FnAttrs(AttrFactory);
TypeResult TrailingReturnType;
@ -5279,10 +5280,14 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
IsCXX11MemberFunction);
// Parse exception-specification[opt].
ESpecType = tryParseExceptionSpecification(ESpecRange,
bool Delayed = D.isFirstDeclarationOfMember() &&
D.isFunctionDeclaratorAFunctionDeclaration();
ESpecType = tryParseExceptionSpecification(Delayed,
ESpecRange,
DynamicExceptions,
DynamicExceptionRanges,
NoexceptExpr);
NoexceptExpr,
ExceptionSpecTokens);
if (ESpecType != EST_None)
EndLoc = ESpecRange.getEnd();
@ -5322,6 +5327,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
DynamicExceptions.size(),
NoexceptExpr.isUsable() ?
NoexceptExpr.get() : nullptr,
ExceptionSpecTokens,
StartLoc, LocalEndLoc, D,
TrailingReturnType),
FnAttrs, EndLoc);

View File

@ -1875,16 +1875,34 @@ AccessSpecifier Parser::getAccessSpecifierIfPresent() const {
}
/// \brief If the given declarator has any parts for which parsing has to be
/// delayed, e.g., default arguments, create a late-parsed method declaration
/// record to handle the parsing at the end of the class definition.
/// delayed, e.g., default arguments or an exception-specification, create a
/// late-parsed method declaration record to handle the parsing at the end of
/// the class definition.
void Parser::HandleMemberFunctionDeclDelays(Declarator& DeclaratorInfo,
Decl *ThisDecl) {
// We just declared a member function. If this member function
// has any default arguments, we'll need to parse them later.
// has any default arguments or an exception-specification, we'll need to
// parse them later.
LateParsedMethodDeclaration *LateMethod = nullptr;
DeclaratorChunk::FunctionTypeInfo &FTI
= DeclaratorInfo.getFunctionTypeInfo();
// If there was a late-parsed exception-specification, hold onto its tokens.
if (FTI.getExceptionSpecType() == EST_Unparsed) {
// Push this method onto the stack of late-parsed method
// declarations.
LateMethod = new LateParsedMethodDeclaration(this, ThisDecl);
getCurrentClass().LateParsedDeclarations.push_back(LateMethod);
LateMethod->TemplateScope = getCurScope()->isTemplateParamScope();
// Stash the exception-specification tokens in the late-pased mthod.
LateMethod->ExceptionSpecTokens = FTI.ExceptionSpecTokens;
FTI.ExceptionSpecTokens = 0;
// Reserve space for the parameters.
LateMethod->DefaultArgs.reserve(FTI.NumParams);
}
for (unsigned ParamIdx = 0; ParamIdx < FTI.NumParams; ++ParamIdx) {
if (LateMethod || FTI.Params[ParamIdx].DefaultArgTokens) {
if (!LateMethod) {
@ -2857,7 +2875,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
// C++11 [class.mem]p2:
// Within the class member-specification, the class is regarded as complete
// within function bodies, default arguments, and
// within function bodies, default arguments, exception-specifications, and
// brace-or-equal-initializers for non-static data members (including such
// things in nested classes).
if (TagDecl && NonNestedClass) {
@ -3076,13 +3094,63 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
/// 'noexcept'
/// 'noexcept' '(' constant-expression ')'
ExceptionSpecificationType
Parser::tryParseExceptionSpecification(
Parser::tryParseExceptionSpecification(bool Delayed,
SourceRange &SpecificationRange,
SmallVectorImpl<ParsedType> &DynamicExceptions,
SmallVectorImpl<SourceRange> &DynamicExceptionRanges,
ExprResult &NoexceptExpr) {
ExprResult &NoexceptExpr,
CachedTokens *&ExceptionSpecTokens) {
ExceptionSpecificationType Result = EST_None;
ExceptionSpecTokens = 0;
// Handle delayed parsing of exception-specifications.
if (Delayed) {
if (Tok.isNot(tok::kw_throw) && Tok.isNot(tok::kw_noexcept))
return EST_None;
// Consume and cache the starting token.
bool IsNoexcept = Tok.is(tok::kw_noexcept);
Token StartTok = Tok;
SpecificationRange = SourceRange(ConsumeToken());
// Check for a '('.
if (!Tok.is(tok::l_paren)) {
// If this is a bare 'noexcept', we're done.
if (IsNoexcept) {
Diag(Tok, diag::warn_cxx98_compat_noexcept_decl);
NoexceptExpr = 0;
return EST_BasicNoexcept;
}
Diag(Tok, diag::err_expected_lparen_after) << "throw";
return EST_DynamicNone;
}
// Cache the tokens for the exception-specification.
ExceptionSpecTokens = new CachedTokens;
ExceptionSpecTokens->push_back(StartTok); // 'throw' or 'noexcept'
ExceptionSpecTokens->push_back(Tok); // '('
SpecificationRange.setEnd(ConsumeParen()); // '('
if (!ConsumeAndStoreUntil(tok::r_paren, *ExceptionSpecTokens,
/*StopAtSemi=*/true,
/*ConsumeFinalToken=*/true)) {
NoexceptExpr = 0;
delete ExceptionSpecTokens;
ExceptionSpecTokens = 0;
return IsNoexcept? EST_BasicNoexcept : EST_DynamicNone;
}
SpecificationRange.setEnd(Tok.getLocation());
// Add the 'stop' token.
Token End;
End.startToken();
End.setKind(tok::cxx_exceptspec_end);
End.setLocation(Tok.getLocation());
ExceptionSpecTokens->push_back(End);
return EST_Unparsed;
}
// See if there's a dynamic specification.
if (Tok.is(tok::kw_throw)) {
Result = ParseDynamicExceptionSpecification(SpecificationRange,

View File

@ -2642,6 +2642,7 @@ ExprResult Parser::ParseBlockLiteralExpression() {
/*ExceptionRanges=*/nullptr,
/*NumExceptions=*/0,
/*NoexceptExpr=*/nullptr,
/*ExceptionSpecTokens=*/nullptr,
CaretLoc, CaretLoc,
ParamInfo),
attrs, CaretLoc);

View File

@ -1061,10 +1061,13 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
SmallVector<ParsedType, 2> DynamicExceptions;
SmallVector<SourceRange, 2> DynamicExceptionRanges;
ExprResult NoexceptExpr;
ESpecType = tryParseExceptionSpecification(ESpecRange,
CachedTokens *ExceptionSpecTokens;
ESpecType = tryParseExceptionSpecification(/*Delayed=*/false,
ESpecRange,
DynamicExceptions,
DynamicExceptionRanges,
NoexceptExpr);
NoexceptExpr,
ExceptionSpecTokens);
if (ESpecType != EST_None)
DeclEndLoc = ESpecRange.getEnd();
@ -1105,6 +1108,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
DynamicExceptions.size(),
NoexceptExpr.isUsable() ?
NoexceptExpr.get() : nullptr,
/*ExceptionSpecTokens*/nullptr,
LParenLoc, FunLocalRangeEnd, D,
TrailingReturnType),
Attr, DeclEndLoc);
@ -1173,6 +1177,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
/*ExceptionRanges=*/nullptr,
/*NumExceptions=*/0,
/*NoexceptExpr=*/nullptr,
/*ExceptionSpecTokens=*/nullptr,
DeclLoc, DeclEndLoc, D,
TrailingReturnType),
Attr, DeclEndLoc);

View File

@ -182,6 +182,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto,
SourceRange *ExceptionRanges,
unsigned NumExceptions,
Expr *NoexceptExpr,
CachedTokens *ExceptionSpecTokens,
SourceLocation LocalRangeBegin,
SourceLocation LocalRangeEnd,
Declarator &TheDeclarator,
@ -219,6 +220,9 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto,
TrailingReturnType.isInvalid();
I.Fun.TrailingReturnType = TrailingReturnType.get();
assert(I.Fun.TypeQuals == TypeQuals && "bitfield overflow");
assert(I.Fun.ExceptionSpecType == ESpecType && "bitfield overflow");
// new[] a parameter array if needed.
if (NumParams) {
// If the 'InlineParams' in Declarator is unused and big enough, put our
@ -255,6 +259,10 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto,
case EST_ComputedNoexcept:
I.Fun.NoexceptExpr = NoexceptExpr;
break;
case EST_Unparsed:
I.Fun.ExceptionSpecTokens = ExceptionSpecTokens;
break;
}
return I;
}

View File

@ -10682,6 +10682,7 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc,
/*ExceptionRanges=*/nullptr,
/*NumExceptions=*/0,
/*NoexceptExpr=*/nullptr,
/*ExceptionSpecTokens=*/nullptr,
Loc, Loc, D),
DS.getAttributes(),
SourceLocation());

View File

@ -5286,6 +5286,12 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
/// C++11 [dcl.fct.def.default]p2.
void Sema::CheckExplicitlyDefaultedMemberExceptionSpec(
CXXMethodDecl *MD, const FunctionProtoType *SpecifiedType) {
// If the exception specification was explicitly specified but hadn't been
// parsed when the method was defaulted, grab it now.
if (SpecifiedType->getExceptionSpecType() == EST_Unparsed)
SpecifiedType =
MD->getTypeSourceInfo()->getType()->castAs<FunctionProtoType>();
// Compute the implicit exception specification.
CallingConv CC = Context.getDefaultCallingConvention(/*IsVariadic=*/false,
/*IsCXXMethod=*/true);
@ -13245,6 +13251,7 @@ bool Sema::checkThisInStaticMemberFunctionExceptionSpec(CXXMethodDecl *Method) {
FindCXXThisExpr Finder(*this);
switch (Proto->getExceptionSpecType()) {
case EST_Unparsed:
case EST_Uninstantiated:
case EST_Unevaluated:
case EST_BasicNoexcept:
@ -13372,6 +13379,45 @@ void Sema::checkExceptionSpecification(
}
}
void Sema::actOnDelayedExceptionSpecification(Decl *MethodD,
ExceptionSpecificationType EST,
SourceRange SpecificationRange,
ArrayRef<ParsedType> DynamicExceptions,
ArrayRef<SourceRange> DynamicExceptionRanges,
Expr *NoexceptExpr) {
if (!MethodD)
return;
// Dig out the method we're referring to.
if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(MethodD))
MethodD = FunTmpl->getTemplatedDecl();
CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(MethodD);
if (!Method)
return;
// Check the exception specification.
llvm::SmallVector<QualType, 4> Exceptions;
FunctionProtoType::ExceptionSpecInfo ESI;
checkExceptionSpecification(/*IsTopLevel*/true, EST, DynamicExceptions,
DynamicExceptionRanges, NoexceptExpr, Exceptions,
ESI);
// Update the exception specification on the function type.
Context.adjustExceptionSpec(Method, ESI, /*AsWritten*/true);
if (Method->isStatic())
checkThisInStaticMemberFunctionExceptionSpec(Method);
if (Method->isVirtual()) {
// Check overrides, which we previously had to delay.
for (CXXMethodDecl::method_iterator O = Method->begin_overridden_methods(),
OEnd = Method->end_overridden_methods();
O != OEnd; ++O)
CheckOverridingFunctionExceptionSpec(Method, *O);
}
}
/// HandleMSProperty - Analyze a __delcspec(property) field of a C++ class.
///
MSPropertyDecl *Sema::HandleMSProperty(Scope *S, RecordDecl *Record,

View File

@ -112,6 +112,11 @@ bool Sema::CheckDistantExceptionSpec(QualType T) {
const FunctionProtoType *
Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
if (FPT->getExceptionSpecType() == EST_Unparsed) {
Diag(Loc, diag::err_exception_spec_not_parsed);
return nullptr;
}
if (!isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
return FPT;
@ -135,16 +140,8 @@ Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
void
Sema::UpdateExceptionSpec(FunctionDecl *FD,
const FunctionProtoType::ExceptionSpecInfo &ESI) {
for (auto *Redecl : FD->redecls()) {
auto *RedeclFD = dyn_cast<FunctionDecl>(Redecl);
const FunctionProtoType *Proto =
RedeclFD->getType()->castAs<FunctionProtoType>();
// Overwrite the exception spec and rebuild the function type.
RedeclFD->setType(Context.getFunctionType(
Proto->getReturnType(), Proto->getParamTypes(),
Proto->getExtProtoInfo().withExceptionSpec(ESI)));
}
for (auto *Redecl : FD->redecls())
Context.adjustExceptionSpec(cast<FunctionDecl>(Redecl), ESI);
// If we've fully resolved the exception specification, notify listeners.
if (!isUnresolvedExceptionSpec(ESI.Type))
@ -790,6 +787,11 @@ bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
return false;
}
}
// If the exception specification hasn't been parsed yet, skip the check.
// We'll get called again once it's been parsed.
if (New->getType()->castAs<FunctionProtoType>()->getExceptionSpecType() ==
EST_Unparsed)
return false;
unsigned DiagID = diag::err_override_exception_spec;
if (getLangOpts().MicrosoftExt)
DiagID = diag::ext_override_exception_spec;

View File

@ -661,27 +661,27 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state,
// ...and *prepend* it to the declarator.
SourceLocation NoLoc;
declarator.AddInnermostTypeInfo(DeclaratorChunk::getFunction(
/*HasProto=*/true,
/*IsAmbiguous=*/false,
/*LParenLoc=*/NoLoc,
/*ArgInfo=*/nullptr,
/*NumArgs=*/0,
/*EllipsisLoc=*/NoLoc,
/*RParenLoc=*/NoLoc,
/*TypeQuals=*/0,
/*RefQualifierIsLvalueRef=*/true,
/*RefQualifierLoc=*/NoLoc,
/*ConstQualifierLoc=*/NoLoc,
/*VolatileQualifierLoc=*/NoLoc,
/*RestrictQualifierLoc=*/NoLoc,
/*MutableLoc=*/NoLoc,
EST_None,
/*ESpecLoc=*/NoLoc,
/*Exceptions=*/nullptr,
/*ExceptionRanges=*/nullptr,
/*NumExceptions=*/0,
/*NoexceptExpr=*/nullptr,
loc, loc, declarator));
/*HasProto=*/true,
/*IsAmbiguous=*/false,
/*LParenLoc=*/NoLoc,
/*ArgInfo=*/nullptr,
/*NumArgs=*/0,
/*EllipsisLoc=*/NoLoc,
/*RParenLoc=*/NoLoc,
/*TypeQuals=*/0,
/*RefQualifierIsLvalueRef=*/true,
/*RefQualifierLoc=*/NoLoc,
/*ConstQualifierLoc=*/NoLoc,
/*VolatileQualifierLoc=*/NoLoc,
/*RestrictQualifierLoc=*/NoLoc,
/*MutableLoc=*/NoLoc, EST_None,
/*ESpecLoc=*/NoLoc,
/*Exceptions=*/nullptr,
/*ExceptionRanges=*/nullptr,
/*NumExceptions=*/0,
/*NoexceptExpr=*/nullptr,
/*ExceptionSpecTokens=*/nullptr,
loc, loc, declarator));
// For consistency, make sure the state still has us as processing
// the decl spec.

View File

@ -2782,10 +2782,8 @@ void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader,
if (FPT && PrevFPT &&
isUnresolvedExceptionSpec(FPT->getExceptionSpecType()) &&
!isUnresolvedExceptionSpec(PrevFPT->getExceptionSpecType())) {
FunctionProtoType::ExtProtoInfo EPI = PrevFPT->getExtProtoInfo();
FD->setType(Reader.Context.getFunctionType(
FPT->getReturnType(), FPT->getParamTypes(),
FPT->getExtProtoInfo().withExceptionSpec(EPI.ExceptionSpec)));
Reader.Context.adjustExceptionSpec(
FD, PrevFPT->getExtProtoInfo().ExceptionSpec);
}
}
}

View File

@ -56,3 +56,33 @@ namespace test3 {
template struct A2<int>;
}
namespace PR12629 {
struct S {
static int (f)() throw();
static int ((((((g))))() throw(U)));
int (*h)() noexcept(false);
static int (&i)() noexcept(true);
static int (*j)() throw(U); // expected-error {{unknown type name 'U'}}
static int (k)() throw(U);
struct U {};
};
static_assert(noexcept(S::f()), "");
static_assert(!noexcept(S::g()), "");
static_assert(!noexcept(S().h()), "");
static_assert(noexcept(S::i()), "");
}
namespace PR12688 {
struct S {
// FIXME: Producing one error saying this can't have the same name
// as the class because it's not a constructor, then producing
// another error saying this can't have a return type because
// it is a constructor, is redundant and inconsistent.
nonsense S() throw (more_nonsense); // \
// expected-error {{'nonsense'}} \
// expected-error {{has the same name as its class}} \
// expected-error {{constructor cannot have a return type}}
};
}

View File

@ -502,15 +502,15 @@ namespace dr436 { // dr436: yes
void f(); // expected-error {{redefinition}}
}
namespace dr437 { // dr437: no
namespace dr437 { // dr437: sup 1308
// This is superseded by 1308, which is in turn superseded by 1330,
// which restores this rule.
template<typename U> struct T : U {}; // expected-error {{incomplete}}
struct S { // expected-note {{not complete}}
template<typename U> struct T : U {};
struct S {
void f() throw(S);
void g() throw(T<S>); // expected-note {{in instantiation of}}
struct U; // expected-note {{forward}}
void h() throw(U); // expected-error {{incomplete}}
void g() throw(T<S>);
struct U;
void h() throw(U);
struct U {};
};
}

View File

@ -136,12 +136,11 @@ namespace Static {
namespace PR12564 {
struct Base {
void bar(Base&) {} // FIXME: expected-note {{here}}
void bar(Base&) {}
};
struct Derived : Base {
// FIXME: This should be accepted.
void foo(Derived& d) noexcept(noexcept(d.bar(d))) {} // expected-error {{cannot bind to a value of unrelated type}}
void foo(Derived& d) noexcept(noexcept(d.bar(d))) {}
};
}

View File

@ -23,7 +23,7 @@ struct array
{
T data[N];
void swap(array& a) noexcept(noexcept(swap(declval<T&>(), declval<T&>())));
void swap(array& a) noexcept(noexcept(::swap(declval<T&>(), declval<T&>())));
};
struct DefaultOnly
@ -38,3 +38,4 @@ int main()
{
array<DefaultOnly, 1> a, b;
}

View File

@ -46,13 +46,12 @@ namespace InClassInitializers {
}
namespace ExceptionSpecification {
// A type is permitted to be used in a dynamic exception specification when it
// is still being defined, but isn't complete within such an exception
// specification.
struct Nested { // expected-note {{not complete}}
// FIXME: This diagnostic is quite useless; we should indicate whose
// exception specification we were looking for and why.
struct Nested {
struct T {
T() noexcept(!noexcept(Nested())); // expected-error{{incomplete type}}
} t;
T() noexcept(!noexcept(Nested()));
} t; // expected-error{{exception specification is not available until end of class definition}}
};
}