forked from OSchip/llvm-project
Implement the last part of C++ [class.mem]p2, delaying the parsing of
exception specifications on member functions until after the closing '}' for the containing class. This allows, for example, a member function to throw an instance of its own class. Fixes PR12564 and a fairly embarassing oversight in our C++98/03 support. llvm-svn: 154844
This commit is contained in:
parent
505a7c818d
commit
433e05306f
|
@ -64,6 +64,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.
|
||||
|
|
|
@ -410,6 +410,8 @@ def ext_ellipsis_exception_spec : Extension<
|
|||
"exception specification of '...' is a Microsoft extension">;
|
||||
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;
|
||||
|
|
|
@ -105,6 +105,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)
|
||||
|
|
|
@ -855,7 +855,7 @@ private:
|
|||
/// argument (C++ [class.mem]p2).
|
||||
struct LateParsedMethodDeclaration : public LateParsedDeclaration {
|
||||
explicit LateParsedMethodDeclaration(Parser *P, Decl *M)
|
||||
: Self(P), Method(M), TemplateScope(false) { }
|
||||
: Self(P), Method(M), TemplateScope(false), ExceptionSpecTokens(0) { }
|
||||
|
||||
virtual void ParseLexedMethodDeclarations();
|
||||
|
||||
|
@ -875,6 +875,10 @@ private:
|
|||
/// method will be stored so that they can be reintroduced into
|
||||
/// scope at the appropriate times.
|
||||
SmallVector<LateParsedDefaultArgument, 8> DefaultArgs;
|
||||
|
||||
/// \brief The set of tokens that make up an exception-specification that
|
||||
/// has not yet been parsed.
|
||||
CachedTokens *ExceptionSpecTokens;
|
||||
};
|
||||
|
||||
/// LateParsedMemberInitializer - An initializer for a non-static class data
|
||||
|
@ -1417,11 +1421,13 @@ private:
|
|||
// C++ 15: C++ Throw Expression
|
||||
ExprResult ParseThrowExpression();
|
||||
|
||||
ExceptionSpecificationType MaybeParseExceptionSpecification(
|
||||
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(
|
||||
|
@ -2102,8 +2108,8 @@ private:
|
|||
ParsingDeclRAIIObject *DiagsFromTParams = 0);
|
||||
void ParseConstructorInitializer(Decl *ConstructorDecl);
|
||||
MemInitResult ParseMemInitializer(Decl *ConstructorDecl);
|
||||
void HandleMemberFunctionDefaultArgs(Declarator& DeclaratorInfo,
|
||||
Decl *ThisDecl);
|
||||
void HandleMemberFunctionDeclDelays(Declarator& DeclaratorInfo,
|
||||
Decl *ThisDecl);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// C++ 10: Derived classes [class.derived]
|
||||
|
|
|
@ -1150,6 +1150,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;
|
||||
};
|
||||
|
||||
/// TrailingReturnType - If this isn't null, it's the trailing return type
|
||||
|
@ -1172,6 +1176,8 @@ struct DeclaratorChunk {
|
|||
delete[] ArgInfo;
|
||||
if (getExceptionSpecType() == EST_Dynamic)
|
||||
delete[] Exceptions;
|
||||
else if (getExceptionSpecType() == EST_Delayed)
|
||||
delete ExceptionSpecTokens;
|
||||
}
|
||||
|
||||
/// isKNRPrototype - Return true if this is a K&R style identifier list,
|
||||
|
@ -1347,6 +1353,7 @@ struct DeclaratorChunk {
|
|||
SourceRange *ExceptionRanges,
|
||||
unsigned NumExceptions,
|
||||
Expr *NoexceptExpr,
|
||||
CachedTokens *ExceptionSpecTokens,
|
||||
SourceLocation LocalRangeBegin,
|
||||
SourceLocation LocalRangeEnd,
|
||||
Declarator &TheDeclarator,
|
||||
|
|
|
@ -901,6 +901,7 @@ public:
|
|||
TypeSourceInfo *GetTypeForDeclaratorCast(Declarator &D, QualType FromTy);
|
||||
TypeSourceInfo *GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
|
||||
TypeSourceInfo *ReturnTypeInfo);
|
||||
|
||||
/// \brief Package the given type and TSI into a ParsedType.
|
||||
ParsedType CreateParsedType(QualType T, TypeSourceInfo *TInfo);
|
||||
DeclarationNameInfo GetNameForDeclarator(Declarator &D);
|
||||
|
@ -3145,6 +3146,25 @@ public:
|
|||
ImplicitExceptionSpecification
|
||||
ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl);
|
||||
|
||||
/// \brief Check the given exception-specification and update the
|
||||
/// extended prototype information with the results.
|
||||
void checkExceptionSpecification(ExceptionSpecificationType EST,
|
||||
ArrayRef<ParsedType> DynamicExceptions,
|
||||
ArrayRef<SourceRange> DynamicExceptionRanges,
|
||||
Expr *NoexceptExpr,
|
||||
llvm::SmallVectorImpl<QualType> &Exceptions,
|
||||
FunctionProtoType::ExtProtoInfo &EPI);
|
||||
|
||||
/// \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,
|
||||
|
@ -3250,13 +3270,17 @@ public:
|
|||
/// special member function.
|
||||
bool isImplicitlyDeleted(FunctionDecl *FD);
|
||||
|
||||
/// \brief Check wither 'this' shows up in the type of a static member
|
||||
/// \brief Check whether 'this' shows up in the type of a static member
|
||||
/// function after the (naturally empty) cv-qualifier-seq would be.
|
||||
///
|
||||
/// \returns true if an error occurred.
|
||||
bool checkThisInStaticMemberFunctionType(CXXMethodDecl *Method);
|
||||
|
||||
/// \brief Check wither 'this' shows up in the attributes of the given
|
||||
|
||||
/// \brief Whether this' shows up in the exception specification of a static
|
||||
/// member function.
|
||||
bool checkThisInStaticMemberFunctionExceptionSpec(CXXMethodDecl *Method);
|
||||
|
||||
/// \brief Check whether 'this' shows up in the attributes of the given
|
||||
/// static member function.
|
||||
///
|
||||
/// \returns true if an error occurred.
|
||||
|
|
|
@ -59,7 +59,7 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS,
|
|||
}
|
||||
}
|
||||
|
||||
HandleMemberFunctionDefaultArgs(D, FnD);
|
||||
HandleMemberFunctionDeclDelays(D, FnD);
|
||||
|
||||
D.complete(FnD);
|
||||
|
||||
|
@ -348,6 +348,77 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
|
|||
LM.DefaultArgs[I].Toks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 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().CPlusPlus0x);
|
||||
|
||||
// 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() : 0);
|
||||
|
||||
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 LM.ExceptionSpecTokens;
|
||||
LM.ExceptionSpecTokens = 0;
|
||||
}
|
||||
|
||||
PrototypeScope.Exit();
|
||||
|
||||
// Finish the delayed C++ method declaration.
|
||||
|
|
|
@ -4197,6 +4197,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||
SmallVector<ParsedType, 2> DynamicExceptions;
|
||||
SmallVector<SourceRange, 2> DynamicExceptionRanges;
|
||||
ExprResult NoexceptExpr;
|
||||
CachedTokens *ExceptionSpecTokens = 0;
|
||||
ParsedAttributes FnAttrs(AttrFactory);
|
||||
ParsedType TrailingReturnType;
|
||||
|
||||
|
@ -4265,10 +4266,16 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||
IsCXX11MemberFunction);
|
||||
|
||||
// Parse exception-specification[opt].
|
||||
ESpecType = MaybeParseExceptionSpecification(ESpecRange,
|
||||
DynamicExceptions,
|
||||
DynamicExceptionRanges,
|
||||
NoexceptExpr);
|
||||
bool Delayed = (D.getContext() == Declarator::MemberContext &&
|
||||
D.getDeclSpec().getStorageClassSpec()
|
||||
!= DeclSpec::SCS_typedef &&
|
||||
!D.getDeclSpec().isFriendSpecified());
|
||||
ESpecType = tryParseExceptionSpecification(Delayed,
|
||||
ESpecRange,
|
||||
DynamicExceptions,
|
||||
DynamicExceptionRanges,
|
||||
NoexceptExpr,
|
||||
ExceptionSpecTokens);
|
||||
if (ESpecType != EST_None)
|
||||
EndLoc = ESpecRange.getEnd();
|
||||
|
||||
|
@ -4303,6 +4310,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||
DynamicExceptions.size(),
|
||||
NoexceptExpr.isUsable() ?
|
||||
NoexceptExpr.get() : 0,
|
||||
ExceptionSpecTokens,
|
||||
Tracker.getOpenLocation(),
|
||||
EndLoc, D,
|
||||
TrailingReturnType),
|
||||
|
@ -4504,7 +4512,6 @@ void Parser::ParseParameterDeclarationClause(
|
|||
// If we're inside a class definition, cache the tokens
|
||||
// corresponding to the default argument. We'll actually parse
|
||||
// them when we see the end of the class definition.
|
||||
// FIXME: Templates will require something similar.
|
||||
// FIXME: Can we use a smart pointer for Toks?
|
||||
DefArgToks = new CachedTokens;
|
||||
|
||||
|
|
|
@ -1534,13 +1534,35 @@ AccessSpecifier Parser::getAccessSpecifierIfPresent() const {
|
|||
}
|
||||
}
|
||||
|
||||
void Parser::HandleMemberFunctionDefaultArgs(Declarator& DeclaratorInfo,
|
||||
Decl *ThisDecl) {
|
||||
/// \brief If the given declarator has any parts for which parsing has to be
|
||||
/// 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 = 0;
|
||||
DeclaratorChunk::FunctionTypeInfo &FTI
|
||||
= DeclaratorInfo.getFunctionTypeInfo();
|
||||
|
||||
// If there was a delayed exception-specification, hold onto its tokens.
|
||||
if (FTI.getExceptionSpecType() == EST_Delayed) {
|
||||
// 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.NumArgs);
|
||||
}
|
||||
|
||||
for (unsigned ParamIdx = 0; ParamIdx < FTI.NumArgs; ++ParamIdx) {
|
||||
if (LateMethod || FTI.ArgInfo[ParamIdx].DefaultArgTokens) {
|
||||
if (!LateMethod) {
|
||||
|
@ -1558,7 +1580,7 @@ void Parser::HandleMemberFunctionDefaultArgs(Declarator& DeclaratorInfo,
|
|||
LateParsedDefaultArgument(FTI.ArgInfo[I].Param));
|
||||
}
|
||||
|
||||
// Add this parameter to the list of parameters (it or may
|
||||
// Add this parameter to the list of parameters (it may or may
|
||||
// not have a default argument).
|
||||
LateMethod->DefaultArgs.push_back(
|
||||
LateParsedDefaultArgument(FTI.ArgInfo[ParamIdx].Param,
|
||||
|
@ -1824,7 +1846,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
|
|||
|
||||
// Parse the first declarator.
|
||||
ParseDeclarator(DeclaratorInfo);
|
||||
// Error parsing the declarator?
|
||||
// Error parsin g the declarator?
|
||||
if (!DeclaratorInfo.hasName()) {
|
||||
// If so, skip until the semi-colon or a }.
|
||||
SkipUntil(tok::r_brace, true, true);
|
||||
|
@ -2046,7 +2068,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
|
|||
if (DeclaratorInfo.isFunctionDeclarator() &&
|
||||
DeclaratorInfo.getDeclSpec().getStorageClassSpec()
|
||||
!= DeclSpec::SCS_typedef) {
|
||||
HandleMemberFunctionDefaultArgs(DeclaratorInfo, ThisDecl);
|
||||
HandleMemberFunctionDeclDelays(DeclaratorInfo, ThisDecl);
|
||||
}
|
||||
|
||||
DeclaratorInfo.complete(ThisDecl);
|
||||
|
@ -2334,13 +2356,11 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
|
|||
T.getCloseLocation(),
|
||||
attrs.getList());
|
||||
|
||||
// C++0x [class.mem]p2: Within the class member-specification, the class is
|
||||
// regarded as complete within function bodies, default arguments, exception-
|
||||
// specifications, and brace-or-equal-initializers for non-static data
|
||||
// members (including such things in nested classes).
|
||||
//
|
||||
// FIXME: Only function bodies and brace-or-equal-initializers are currently
|
||||
// handled. Fix the others!
|
||||
// C++11 [class.mem]p2:
|
||||
// Within the class member-specification, the class is regarded as complete
|
||||
// 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) {
|
||||
// We are not inside a nested class. This class and its nested classes
|
||||
// are complete and we can parse the delayed portions of method
|
||||
|
@ -2535,12 +2555,63 @@ Parser::MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
|
|||
/// 'noexcept'
|
||||
/// 'noexcept' '(' constant-expression ')'
|
||||
ExceptionSpecificationType
|
||||
Parser::MaybeParseExceptionSpecification(SourceRange &SpecificationRange,
|
||||
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_Delayed;
|
||||
}
|
||||
|
||||
// See if there's a dynamic specification.
|
||||
if (Tok.is(tok::kw_throw)) {
|
||||
Result = ParseDynamicExceptionSpecification(SpecificationRange,
|
||||
|
|
|
@ -2392,7 +2392,7 @@ ExprResult Parser::ParseBlockLiteralExpression() {
|
|||
SourceLocation(),
|
||||
EST_None,
|
||||
SourceLocation(),
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
CaretLoc, CaretLoc,
|
||||
ParamInfo),
|
||||
attrs, CaretLoc);
|
||||
|
|
|
@ -780,10 +780,13 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
llvm::SmallVector<ParsedType, 2> DynamicExceptions;
|
||||
llvm::SmallVector<SourceRange, 2> DynamicExceptionRanges;
|
||||
ExprResult NoexceptExpr;
|
||||
ESpecType = MaybeParseExceptionSpecification(ESpecRange,
|
||||
DynamicExceptions,
|
||||
DynamicExceptionRanges,
|
||||
NoexceptExpr);
|
||||
CachedTokens *ExceptionSpecTokens;
|
||||
ESpecType = tryParseExceptionSpecification(/*Delayed=*/false,
|
||||
ESpecRange,
|
||||
DynamicExceptions,
|
||||
DynamicExceptionRanges,
|
||||
NoexceptExpr,
|
||||
ExceptionSpecTokens);
|
||||
|
||||
if (ESpecType != EST_None)
|
||||
DeclEndLoc = ESpecRange.getEnd();
|
||||
|
@ -818,6 +821,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
DynamicExceptions.size(),
|
||||
NoexceptExpr.isUsable() ?
|
||||
NoexceptExpr.get() : 0,
|
||||
0,
|
||||
DeclLoc, DeclEndLoc, D,
|
||||
TrailingReturnType),
|
||||
Attr, DeclEndLoc);
|
||||
|
@ -863,6 +867,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
/*ExceptionRanges=*/0,
|
||||
/*NumExceptions=*/0,
|
||||
/*NoexceptExpr=*/0,
|
||||
/*ExceptionSpecTokens=*/0,
|
||||
DeclLoc, DeclEndLoc, D,
|
||||
TrailingReturnType),
|
||||
Attr, DeclEndLoc);
|
||||
|
|
|
@ -162,6 +162,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
|
|||
SourceRange *ExceptionRanges,
|
||||
unsigned NumExceptions,
|
||||
Expr *NoexceptExpr,
|
||||
CachedTokens *ExceptionSpecTokens,
|
||||
SourceLocation LocalRangeBegin,
|
||||
SourceLocation LocalRangeEnd,
|
||||
Declarator &TheDeclarator,
|
||||
|
@ -226,6 +227,10 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
|
|||
case EST_ComputedNoexcept:
|
||||
I.Fun.NoexceptExpr = NoexceptExpr;
|
||||
break;
|
||||
|
||||
case EST_Delayed:
|
||||
I.Fun.ExceptionSpecTokens = ExceptionSpecTokens;
|
||||
break;
|
||||
}
|
||||
return I;
|
||||
}
|
||||
|
|
|
@ -4471,6 +4471,11 @@ static bool FindOverriddenMethod(const CXXBaseSpecifier *Specifier,
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool hasDelayedExceptionSpec(CXXMethodDecl *Method) {
|
||||
const FunctionProtoType *Proto =Method->getType()->getAs<FunctionProtoType>();
|
||||
return Proto && Proto->getExceptionSpecType() == EST_Delayed;
|
||||
}
|
||||
|
||||
/// AddOverriddenMethods - See if a method overrides any in the base classes,
|
||||
/// and if so, check that it's a valid override and remember it.
|
||||
bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
|
||||
|
@ -4486,7 +4491,8 @@ bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
|
|||
if (CXXMethodDecl *OldMD = dyn_cast<CXXMethodDecl>(*I)) {
|
||||
MD->addOverriddenMethod(OldMD->getCanonicalDecl());
|
||||
if (!CheckOverridingFunctionReturnType(MD, OldMD) &&
|
||||
!CheckOverridingFunctionExceptionSpec(MD, OldMD) &&
|
||||
(hasDelayedExceptionSpec(MD) ||
|
||||
!CheckOverridingFunctionExceptionSpec(MD, OldMD)) &&
|
||||
!CheckIfOverriddenFunctionIsMarkedFinal(MD, OldMD)) {
|
||||
AddedAny = true;
|
||||
}
|
||||
|
@ -7626,7 +7632,7 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc,
|
|||
SourceLocation(), SourceLocation(),
|
||||
SourceLocation(),
|
||||
EST_None, SourceLocation(),
|
||||
0, 0, 0, 0, Loc, Loc, D),
|
||||
0, 0, 0, 0, 0, Loc, Loc, D),
|
||||
DS.getAttributes(),
|
||||
SourceLocation());
|
||||
D.SetIdentifier(&II, Loc);
|
||||
|
|
|
@ -11096,6 +11096,25 @@ bool Sema::checkThisInStaticMemberFunctionType(CXXMethodDecl *Method) {
|
|||
return true;
|
||||
|
||||
// Check the exception specification.
|
||||
if (checkThisInStaticMemberFunctionExceptionSpec(Method))
|
||||
return true;
|
||||
|
||||
return checkThisInStaticMemberFunctionAttributes(Method);
|
||||
}
|
||||
|
||||
bool Sema::checkThisInStaticMemberFunctionExceptionSpec(CXXMethodDecl *Method) {
|
||||
TypeSourceInfo *TSInfo = Method->getTypeSourceInfo();
|
||||
if (!TSInfo)
|
||||
return false;
|
||||
|
||||
TypeLoc TL = TSInfo->getTypeLoc();
|
||||
FunctionProtoTypeLoc *ProtoTL = dyn_cast<FunctionProtoTypeLoc>(&TL);
|
||||
if (!ProtoTL)
|
||||
return false;
|
||||
|
||||
const FunctionProtoType *Proto = ProtoTL->getTypePtr();
|
||||
FindCXXThisExpr Finder(*this);
|
||||
|
||||
switch (Proto->getExceptionSpecType()) {
|
||||
case EST_BasicNoexcept:
|
||||
case EST_Delayed:
|
||||
|
@ -11103,22 +11122,22 @@ bool Sema::checkThisInStaticMemberFunctionType(CXXMethodDecl *Method) {
|
|||
case EST_MSAny:
|
||||
case EST_None:
|
||||
break;
|
||||
|
||||
|
||||
case EST_ComputedNoexcept:
|
||||
if (!Finder.TraverseStmt(Proto->getNoexceptExpr()))
|
||||
return true;
|
||||
|
||||
|
||||
case EST_Dynamic:
|
||||
for (FunctionProtoType::exception_iterator E = Proto->exception_begin(),
|
||||
EEnd = Proto->exception_end();
|
||||
EEnd = Proto->exception_end();
|
||||
E != EEnd; ++E) {
|
||||
if (!Finder.TraverseType(*E))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return checkThisInStaticMemberFunctionAttributes(Method);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sema::checkThisInStaticMemberFunctionAttributes(CXXMethodDecl *Method) {
|
||||
|
@ -11177,6 +11196,122 @@ bool Sema::checkThisInStaticMemberFunctionAttributes(CXXMethodDecl *Method) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Sema::checkExceptionSpecification(ExceptionSpecificationType EST,
|
||||
ArrayRef<ParsedType> DynamicExceptions,
|
||||
ArrayRef<SourceRange> DynamicExceptionRanges,
|
||||
Expr *NoexceptExpr,
|
||||
llvm::SmallVectorImpl<QualType> &Exceptions,
|
||||
FunctionProtoType::ExtProtoInfo &EPI) {
|
||||
Exceptions.clear();
|
||||
EPI.ExceptionSpecType = EST;
|
||||
if (EST == EST_Dynamic) {
|
||||
Exceptions.reserve(DynamicExceptions.size());
|
||||
for (unsigned ei = 0, ee = DynamicExceptions.size(); ei != ee; ++ei) {
|
||||
// FIXME: Preserve type source info.
|
||||
QualType ET = GetTypeFromParser(DynamicExceptions[ei]);
|
||||
|
||||
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
|
||||
collectUnexpandedParameterPacks(ET, Unexpanded);
|
||||
if (!Unexpanded.empty()) {
|
||||
DiagnoseUnexpandedParameterPacks(DynamicExceptionRanges[ei].getBegin(),
|
||||
UPPC_ExceptionType,
|
||||
Unexpanded);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check that the type is valid for an exception spec, and
|
||||
// drop it if not.
|
||||
if (!CheckSpecifiedExceptionType(ET, DynamicExceptionRanges[ei]))
|
||||
Exceptions.push_back(ET);
|
||||
}
|
||||
EPI.NumExceptions = Exceptions.size();
|
||||
EPI.Exceptions = Exceptions.data();
|
||||
return;
|
||||
}
|
||||
|
||||
if (EST == EST_ComputedNoexcept) {
|
||||
// If an error occurred, there's no expression here.
|
||||
if (NoexceptExpr) {
|
||||
assert((NoexceptExpr->isTypeDependent() ||
|
||||
NoexceptExpr->getType()->getCanonicalTypeUnqualified() ==
|
||||
Context.BoolTy) &&
|
||||
"Parser should have made sure that the expression is boolean");
|
||||
if (NoexceptExpr && DiagnoseUnexpandedParameterPack(NoexceptExpr)) {
|
||||
EPI.ExceptionSpecType = EST_BasicNoexcept;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NoexceptExpr->isValueDependent())
|
||||
NoexceptExpr = VerifyIntegerConstantExpression(NoexceptExpr, 0,
|
||||
PDiag(diag::err_noexcept_needs_constant_expression),
|
||||
/*AllowFold*/ false).take();
|
||||
EPI.NoexceptExpr = NoexceptExpr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
CXXMethodDecl *Method = 0;
|
||||
if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(MethodD))
|
||||
Method = dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl());
|
||||
else
|
||||
Method = dyn_cast<CXXMethodDecl>(MethodD);
|
||||
|
||||
if (!Method)
|
||||
return;
|
||||
|
||||
// Dig out the prototype. This should never fail.
|
||||
const FunctionProtoType *Proto
|
||||
= dyn_cast<FunctionProtoType>(Method->getType());
|
||||
if (!Proto)
|
||||
return;
|
||||
|
||||
// Check the exception specification.
|
||||
llvm::SmallVector<QualType, 4> Exceptions;
|
||||
FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo();
|
||||
checkExceptionSpecification(EST, DynamicExceptions, DynamicExceptionRanges,
|
||||
NoexceptExpr, Exceptions, EPI);
|
||||
|
||||
// Rebuild the function type.
|
||||
QualType T = Context.getFunctionType(Proto->getResultType(),
|
||||
Proto->arg_type_begin(),
|
||||
Proto->getNumArgs(),
|
||||
EPI);
|
||||
if (TypeSourceInfo *TSInfo = Method->getTypeSourceInfo()) {
|
||||
// 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(T)
|
||||
== TypeLoc::getFullDataSizeForType(Method->getType()) &&
|
||||
"TypeLoc size mismatch with delayed exception specification");
|
||||
TSInfo->overrideType(T);
|
||||
}
|
||||
|
||||
Method->setType(T);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// IdentifyCUDATarget - Determine the CUDA compilation target for this function
|
||||
Sema::CUDAFunctionTarget Sema::IdentifyCUDATarget(const FunctionDecl *D) {
|
||||
// Implicitly declared functions (e.g. copy constructors) are
|
||||
|
|
|
@ -561,7 +561,7 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state,
|
|||
/*const qualifier*/SourceLocation(),
|
||||
/*volatile qualifier*/SourceLocation(),
|
||||
/*mutable qualifier*/SourceLocation(),
|
||||
/*EH*/ EST_None, SourceLocation(), 0, 0, 0, 0,
|
||||
/*EH*/ EST_None, SourceLocation(), 0, 0, 0, 0, 0,
|
||||
/*parens*/ loc, loc,
|
||||
declarator));
|
||||
|
||||
|
@ -2371,34 +2371,33 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
|||
EPI.ConsumedArguments = ConsumedArguments.data();
|
||||
|
||||
SmallVector<QualType, 4> Exceptions;
|
||||
EPI.ExceptionSpecType = FTI.getExceptionSpecType();
|
||||
SmallVector<ParsedType, 2> DynamicExceptions;
|
||||
SmallVector<SourceRange, 2> DynamicExceptionRanges;
|
||||
Expr *NoexceptExpr = 0;
|
||||
|
||||
if (FTI.getExceptionSpecType() == EST_Dynamic) {
|
||||
Exceptions.reserve(FTI.NumExceptions);
|
||||
for (unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) {
|
||||
// FIXME: Preserve type source info.
|
||||
QualType ET = S.GetTypeFromParser(FTI.Exceptions[ei].Ty);
|
||||
// Check that the type is valid for an exception spec, and
|
||||
// drop it if not.
|
||||
if (!S.CheckSpecifiedExceptionType(ET, FTI.Exceptions[ei].Range))
|
||||
Exceptions.push_back(ET);
|
||||
// FIXME: It's rather inefficient to have to split into two vectors
|
||||
// here.
|
||||
unsigned N = FTI.NumExceptions;
|
||||
DynamicExceptions.reserve(N);
|
||||
DynamicExceptionRanges.reserve(N);
|
||||
for (unsigned I = 0; I != N; ++I) {
|
||||
DynamicExceptions.push_back(FTI.Exceptions[I].Ty);
|
||||
DynamicExceptionRanges.push_back(FTI.Exceptions[I].Range);
|
||||
}
|
||||
EPI.NumExceptions = Exceptions.size();
|
||||
EPI.Exceptions = Exceptions.data();
|
||||
} else if (FTI.getExceptionSpecType() == EST_ComputedNoexcept) {
|
||||
// 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");
|
||||
if (!NoexceptExpr->isValueDependent())
|
||||
NoexceptExpr = S.VerifyIntegerConstantExpression(NoexceptExpr, 0,
|
||||
S.PDiag(diag::err_noexcept_needs_constant_expression),
|
||||
/*AllowFold*/ false).take();
|
||||
EPI.NoexceptExpr = NoexceptExpr;
|
||||
}
|
||||
} else if (FTI.getExceptionSpecType() == EST_None &&
|
||||
ImplicitlyNoexcept && chunkIndex == 0) {
|
||||
NoexceptExpr = FTI.NoexceptExpr;
|
||||
}
|
||||
|
||||
S.checkExceptionSpecification(FTI.getExceptionSpecType(),
|
||||
DynamicExceptions,
|
||||
DynamicExceptionRanges,
|
||||
NoexceptExpr,
|
||||
Exceptions,
|
||||
EPI);
|
||||
|
||||
if (FTI.getExceptionSpecType() == EST_None &&
|
||||
ImplicitlyNoexcept && chunkIndex == 0) {
|
||||
// Only the outermost chunk is marked noexcept, of course.
|
||||
EPI.ExceptionSpecType = EST_BasicNoexcept;
|
||||
}
|
||||
|
|
|
@ -29,3 +29,30 @@ namespace test2 {
|
|||
A<int> x;
|
||||
};
|
||||
}
|
||||
|
||||
namespace test3 {
|
||||
struct A {
|
||||
struct B {
|
||||
void f() throw(A);
|
||||
void g() throw(B);
|
||||
};
|
||||
|
||||
void f() throw(A);
|
||||
void g() throw(B);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct A2 {
|
||||
struct B {
|
||||
void f1() throw(A2);
|
||||
void f2() throw(A2<T>);
|
||||
void g() throw(B);
|
||||
};
|
||||
|
||||
void f1() throw(A2);
|
||||
void f2() throw(A2<T>);
|
||||
void g() throw(B);
|
||||
};
|
||||
|
||||
template struct A2<int>;
|
||||
}
|
||||
|
|
|
@ -88,3 +88,13 @@ namespace Static {
|
|||
X2<int>().g(0);
|
||||
}
|
||||
}
|
||||
|
||||
namespace PR12564 {
|
||||
struct Base {
|
||||
void bar(Base&) {}
|
||||
};
|
||||
|
||||
struct Derived : Base {
|
||||
void foo(Derived& d) noexcept(noexcept(d.bar(d))) {}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ template<typename T, typename... Types>
|
|||
// FIXME: this should test that the diagnostic reads "type contains..."
|
||||
struct alignas(Types) TestUnexpandedDecls : T{ // expected-error{{expression contains unexpanded parameter pack 'Types'}}
|
||||
void member_function(Types); // expected-error{{declaration type contains unexpanded parameter pack 'Types'}}
|
||||
void member_function () throw(Types); // expected-error{{declaration type contains unexpanded parameter pack 'Types'}}
|
||||
void member_function () throw(Types); // expected-error{{exception type contains unexpanded parameter pack 'Types'}}
|
||||
operator Types() const; // expected-error{{declaration type contains unexpanded parameter pack 'Types'}}
|
||||
Types data_member; // expected-error{{data member type contains unexpanded parameter pack 'Types'}}
|
||||
static Types static_data_member; // expected-error{{declaration type contains unexpanded parameter pack 'Types'}}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -39,20 +39,14 @@ namespace InClassInitializers {
|
|||
bool z = noexcept(Nested::Inner());
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// The same problem arises in delayed parsing of exception specifications,
|
||||
// which clang does not yet support.
|
||||
namespace ExceptionSpecification {
|
||||
struct Nested { // expected-note {{not complete}}
|
||||
struct Nested {
|
||||
struct T {
|
||||
T() noexcept(!noexcept(Nested())); // expected-error {{incomplete type}}
|
||||
T() noexcept(!noexcept(Nested())); // expected-error{{exception specification is not available until end of class definition}}
|
||||
} t;
|
||||
};
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// The same problem arises in delayed parsing of default arguments,
|
||||
// which clang does not yet support.
|
||||
namespace DefaultArgument {
|
||||
struct Default {
|
||||
struct T {
|
||||
|
|
Loading…
Reference in New Issue