diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index d2157d34a55a..ef87fa6dd90a 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -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; diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 017f5b80247e..b946636ebdf0 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -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. diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 5bdeb22dc68b..711ce0c5aff4 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -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(RefQualifier); + return static_cast(FunctionTypeBits.RefQualifier); } typedef const QualType *param_type_iterator; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index eb03f5a3aa26..6bf19108b16d 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -496,6 +496,8 @@ def ext_ellipsis_exception_spec : Extension< InGroup; 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, DefaultIgnore; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 33e28c7b8b65..4a528c4c363f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/clang/include/clang/Basic/ExceptionSpecificationType.h b/clang/include/clang/Basic/ExceptionSpecificationType.h index edd89ec709d7..132b5ba1e5a7 100644 --- a/clang/include/clang/Basic/ExceptionSpecificationType.h +++ b/clang/include/clang/Basic/ExceptionSpecificationType.h @@ -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) { diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index c96b8eb4d6df..c63dea0fa587 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -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) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 8368f04d7a38..9a29edb72238 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1453,10 +1453,12 @@ private: ExprResult ParseThrowExpression(); ExceptionSpecificationType tryParseExceptionSpecification( + bool Delayed, SourceRange &SpecificationRange, SmallVectorImpl &DynamicExceptions, SmallVectorImpl &DynamicExceptionRanges, - ExprResult &NoexceptExpr); + ExprResult &NoexceptExpr, + CachedTokens *&ExceptionSpecTokens); // EndLoc is filled with the location of the last token of the specification. ExceptionSpecificationType ParseDynamicExceptionSpecification( diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index e5f6bb3d7fdc..43fcde1abfe6 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -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, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index cedf81202179..5841b07911d3 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4106,6 +4106,16 @@ public: SmallVectorImpl &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 DynamicExceptions, + ArrayRef 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, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index ae72c5853ce3..1804c0064760 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -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(Orig)) + return Context.getParenType( + getFunctionTypeWithExceptionSpec(Context, PT->getInnerType(), ESI)); + + // Might have a calling-convention attribute. + if (auto *AT = dyn_cast(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(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 { diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 19a3add700d5..abceb8af41fa 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1593,7 +1593,7 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) { FunctionProtoType::FunctionProtoType(QualType result, ArrayRef 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 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(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) + diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index 3571d5ab4135..2a2f3b10461c 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -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(LM.Method)) + Method = cast(FunTmpl->getTemplatedDecl()); + else + Method = cast(LM.Method); + + Sema::CXXThisScopeRAII ThisScope(Actions, Method->getParent(), + Method->getTypeQualifiers(), + getLangOpts().CPlusPlus11); + + // Parse the exception-specification. + SourceRange SpecificationRange; + SmallVector DynamicExceptions; + SmallVector 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. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 66b98c2c4da9..8fbe7833a1ff 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5193,6 +5193,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, SmallVector DynamicExceptions; SmallVector 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); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 7c98d70b6837..91c1372c6339 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -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 &DynamicExceptions, SmallVectorImpl &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, diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index f4eef8117554..b4ba0bc8d8ca 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2642,6 +2642,7 @@ ExprResult Parser::ParseBlockLiteralExpression() { /*ExceptionRanges=*/nullptr, /*NumExceptions=*/0, /*NoexceptExpr=*/nullptr, + /*ExceptionSpecTokens=*/nullptr, CaretLoc, CaretLoc, ParamInfo), attrs, CaretLoc); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 33ebb197704c..fbe5de141678 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1061,10 +1061,13 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( SmallVector DynamicExceptions; SmallVector 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); diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 6e77d2769fac..7b2e7ffbaa49 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -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; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 109c01c88926..bc4d458aca90 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10682,6 +10682,7 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, /*ExceptionRanges=*/nullptr, /*NumExceptions=*/0, /*NoexceptExpr=*/nullptr, + /*ExceptionSpecTokens=*/nullptr, Loc, Loc, D), DS.getAttributes(), SourceLocation()); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index d4cb2fe35044..925bd99051e7 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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(); + // 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 DynamicExceptions, + ArrayRef DynamicExceptionRanges, + Expr *NoexceptExpr) { + if (!MethodD) + return; + + // Dig out the method we're referring to. + if (FunctionTemplateDecl *FunTmpl = dyn_cast(MethodD)) + MethodD = FunTmpl->getTemplatedDecl(); + + CXXMethodDecl *Method = dyn_cast(MethodD); + if (!Method) + return; + + // Check the exception specification. + llvm::SmallVector 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, diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index abac6134cc90..c35de6b8edaa 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -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(Redecl); - const FunctionProtoType *Proto = - RedeclFD->getType()->castAs(); - - // 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(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()->getExceptionSpecType() == + EST_Unparsed) + return false; unsigned DiagID = diag::err_override_exception_spec; if (getLangOpts().MicrosoftExt) DiagID = diag::ext_override_exception_spec; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 87bc3140ae72..8c2f917cfdb2 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -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. diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 0fbbf18c23bb..75ac2c342271 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -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); } } } diff --git a/clang/test/CXX/class/class.mem/p2.cpp b/clang/test/CXX/class/class.mem/p2.cpp index 4aa4a5c6f1b7..d45c03860654 100644 --- a/clang/test/CXX/class/class.mem/p2.cpp +++ b/clang/test/CXX/class/class.mem/p2.cpp @@ -56,3 +56,33 @@ namespace test3 { template struct A2; } + +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}} + }; +} diff --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp index 2b25814b736a..42c6774dd186 100644 --- a/clang/test/CXX/drs/dr4xx.cpp +++ b/clang/test/CXX/drs/dr4xx.cpp @@ -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 struct T : U {}; // expected-error {{incomplete}} - struct S { // expected-note {{not complete}} + template struct T : U {}; + struct S { void f() throw(S); - void g() throw(T); // expected-note {{in instantiation of}} - struct U; // expected-note {{forward}} - void h() throw(U); // expected-error {{incomplete}} + void g() throw(T); + struct U; + void h() throw(U); struct U {}; }; } diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp index fd90482ae8d2..0e948ce00031 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp @@ -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))) {} }; } diff --git a/clang/test/SemaCXX/dependent-noexcept-unevaluated.cpp b/clang/test/SemaCXX/dependent-noexcept-unevaluated.cpp index 0a3a8bb250bc..fad8d0918d53 100644 --- a/clang/test/SemaCXX/dependent-noexcept-unevaluated.cpp +++ b/clang/test/SemaCXX/dependent-noexcept-unevaluated.cpp @@ -23,7 +23,7 @@ struct array { T data[N]; - void swap(array& a) noexcept(noexcept(swap(declval(), declval()))); + void swap(array& a) noexcept(noexcept(::swap(declval(), declval()))); }; struct DefaultOnly @@ -38,3 +38,4 @@ int main() { array a, b; } + diff --git a/clang/test/SemaCXX/implicit-exception-spec.cpp b/clang/test/SemaCXX/implicit-exception-spec.cpp index 6864f29dae30..d9532580bd1d 100644 --- a/clang/test/SemaCXX/implicit-exception-spec.cpp +++ b/clang/test/SemaCXX/implicit-exception-spec.cpp @@ -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}} }; }