From ef65f06e8e440aec541442cfd73a8a836e9bc842 Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Fri, 29 May 2009 18:02:33 +0000 Subject: [PATCH] Reject incomplete types in exception specs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@72580 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 3 ++ include/clang/Parse/DeclSpec.h | 15 ++++-- include/clang/Parse/Parser.h | 5 +- lib/Parse/DeclSpec.cpp | 9 ++-- lib/Parse/ParseDecl.cpp | 42 ++++++++------- lib/Parse/ParseDeclCXX.cpp | 12 +++-- lib/Parse/ParseExpr.cpp | 2 +- lib/Sema/Sema.h | 1 + lib/Sema/SemaDecl.cpp | 2 +- lib/Sema/SemaType.cpp | 60 +++++++++++++++++++--- test/SemaCXX/exception-spec.cpp | 10 ++++ 11 files changed, 122 insertions(+), 39 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index ed64f8a3d1..946b8003be 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -272,6 +272,9 @@ def err_deleted_decl_not_first : Error< def err_distant_exception_spec : Error< "exception specifications are not allowed beyond a single level " "of indirection">; +def err_incomplete_in_exception_spec : Error< + "%select{|pointer to |member pointer to |reference to }1incomplete type %0 " + "is not allowed in exception specification">; // C++ access checking def err_class_redeclared_with_different_access : Error< diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index 9cccd5ebaf..1350c904a2 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -518,7 +518,12 @@ struct DeclaratorChunk { : Ident(ident), IdentLoc(iloc), Param(param), DefaultArgTokens(DefArgTokens) {} }; - + + struct TypeAndRange { + ActionBase::TypeTy *Ty; + SourceRange Range; + }; + struct FunctionTypeInfo { /// hasPrototype - This is true if the function had at least one typed /// argument. If the function is () or (a,b,c), then it has no prototype, @@ -559,9 +564,10 @@ struct DeclaratorChunk { /// there are no arguments specified. ParamInfo *ArgInfo; - /// Exceptions - This is a pointer to a new[]'d array of TypeTy pointers - /// that contains the types in the function's exception specification. - ActionBase::TypeTy **Exceptions; + /// Exceptions - This is a pointer to a new[]'d array of TypeAndRange + /// objects that contain the types in the function's exception + /// specification and their locations. + TypeAndRange *Exceptions; /// freeArgs - reset the argument list to having zero arguments. This is /// used in various places for error recovery. @@ -700,6 +706,7 @@ struct DeclaratorChunk { unsigned TypeQuals, bool hasExceptionSpec, bool hasAnyExceptionSpec, ActionBase::TypeTy **Exceptions, + SourceRange *ExceptionRanges, unsigned NumExceptions, SourceLocation Loc, Declarator &TheDeclarator); diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 9750cab5e7..26e37e2806 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -772,7 +772,8 @@ private: OwningExprResult ParseThrowExpression(); // EndLoc is filled with the location of the last token of the specification. bool ParseExceptionSpecification(SourceLocation &EndLoc, - std::vector &Exceptions, + llvm::SmallVector &Exceptions, + llvm::SmallVector &Ranges, bool &hasAnyExceptionSpec); //===--------------------------------------------------------------------===// @@ -1057,7 +1058,7 @@ private: TPResult TryParseFunctionDeclarator(); TPResult TryParseBracketDeclarator(); - TypeResult ParseTypeName(); + TypeResult ParseTypeName(SourceRange *Range = 0); void ParseBlockId(); // EndLoc, if non-NULL, is filled with the location of the last token of // the attribute list. diff --git a/lib/Parse/DeclSpec.cpp b/lib/Parse/DeclSpec.cpp index 2a765d2124..f3ff0c6379 100644 --- a/lib/Parse/DeclSpec.cpp +++ b/lib/Parse/DeclSpec.cpp @@ -35,6 +35,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, bool hasExceptionSpec, bool hasAnyExceptionSpec, ActionBase::TypeTy **Exceptions, + SourceRange *ExceptionRanges, unsigned NumExceptions, SourceLocation Loc, Declarator &TheDeclarator) { @@ -72,9 +73,11 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, } // new[] an exception array if needed if (NumExceptions) { - I.Fun.Exceptions = new ActionBase::TypeTy*[NumExceptions]; - memcpy(I.Fun.Exceptions, Exceptions, - sizeof(ActionBase::TypeTy*)*NumExceptions); + I.Fun.Exceptions = new DeclaratorChunk::TypeAndRange[NumExceptions]; + for (unsigned i = 0; i != NumExceptions; ++i) { + I.Fun.Exceptions[i].Ty = Exceptions[i]; + I.Fun.Exceptions[i].Range = ExceptionRanges[i]; + } } return I; } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b667014efc..36ebec33b3 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -27,15 +27,17 @@ using namespace clang; /// specifier-qualifier-list abstract-declarator[opt] /// /// Called type-id in C++. -Action::TypeResult Parser::ParseTypeName() { +Action::TypeResult Parser::ParseTypeName(SourceRange *Range) { // Parse the common declaration-specifiers piece. DeclSpec DS; ParseSpecifierQualifierList(DS); - + // Parse the abstract-declarator, if present. Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); ParseDeclarator(DeclaratorInfo); - + if (Range) + *Range = DeclaratorInfo.getSourceRange(); + if (DeclaratorInfo.isInvalidType()) return true; @@ -2273,9 +2275,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, DeclSpec DS; bool hasExceptionSpec = false; bool hasAnyExceptionSpec = false; - // FIXME: Does an empty vector ever allocate? Exception specifications are - // extremely rare, so we want something like a SmallVector. :-) - std::vector Exceptions; + llvm::SmallVector Exceptions; + llvm::SmallVector ExceptionRanges; if (getLang().CPlusPlus) { ParseTypeQualifierListOpt(DS, false /*no attributes*/); if (!DS.getSourceRange().getEnd().isInvalid()) @@ -2284,7 +2285,10 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, // Parse exception-specification[opt]. if (Tok.is(tok::kw_throw)) { hasExceptionSpec = true; - ParseExceptionSpecification(Loc, Exceptions, hasAnyExceptionSpec); + ParseExceptionSpecification(Loc, Exceptions, ExceptionRanges, + hasAnyExceptionSpec); + assert(Exceptions.size() == ExceptionRanges.size() && + "Produced different number of exception types and ranges."); } } @@ -2297,14 +2301,14 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, DS.getTypeQualifiers(), hasExceptionSpec, hasAnyExceptionSpec, - Exceptions.empty() ? 0 : - &Exceptions[0], + Exceptions.data(), + ExceptionRanges.data(), Exceptions.size(), LParenLoc, D), Loc); return; - } - + } + // Alternatively, this parameter list may be an identifier list form for a // K&R-style function: void foo(a,b,c) if (!getLang().CPlusPlus && Tok.is(tok::identifier)) { @@ -2445,9 +2449,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, DeclSpec DS; bool hasExceptionSpec = false; bool hasAnyExceptionSpec = false; - // FIXME: Does an empty vector ever allocate? Exception specifications are - // extremely rare, so we want something like a SmallVector. :-) - std::vector Exceptions; + llvm::SmallVector Exceptions; + llvm::SmallVector ExceptionRanges; if (getLang().CPlusPlus) { // Parse cv-qualifier-seq[opt]. ParseTypeQualifierListOpt(DS, false /*no attributes*/); @@ -2457,7 +2460,10 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, // Parse exception-specification[opt]. if (Tok.is(tok::kw_throw)) { hasExceptionSpec = true; - ParseExceptionSpecification(Loc, Exceptions, hasAnyExceptionSpec); + ParseExceptionSpecification(Loc, Exceptions, ExceptionRanges, + hasAnyExceptionSpec); + assert(Exceptions.size() == ExceptionRanges.size() && + "Produced different number of exception types and ranges."); } } @@ -2468,8 +2474,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, DS.getTypeQualifiers(), hasExceptionSpec, hasAnyExceptionSpec, - Exceptions.empty() ? 0 : - &Exceptions[0], + Exceptions.data(), + ExceptionRanges.data(), Exceptions.size(), LParenLoc, D), Loc); } @@ -2545,7 +2551,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList(SourceLocation LParenLoc, SourceLocation(), &ParamInfo[0], ParamInfo.size(), /*TypeQuals*/0, - /*exception*/false, false, 0, 0, + /*exception*/false, false, 0, 0, 0, LParenLoc, D), RLoc); } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 7cbd72bed4..809dc10c3a 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1191,7 +1191,10 @@ Parser::MemInitResult Parser::ParseMemInitializer(DeclPtrTy ConstructorDecl) { /// type-id-list ',' type-id /// bool Parser::ParseExceptionSpecification(SourceLocation &EndLoc, - std::vector &Exceptions, + llvm::SmallVector + &Exceptions, + llvm::SmallVector + &Ranges, bool &hasAnyExceptionSpec) { assert(Tok.is(tok::kw_throw) && "expected throw"); @@ -1214,10 +1217,13 @@ bool Parser::ParseExceptionSpecification(SourceLocation &EndLoc, } // Parse the sequence of type-ids. + SourceRange Range; while (Tok.isNot(tok::r_paren)) { - TypeResult Res(ParseTypeName()); - if (!Res.isInvalid()) + TypeResult Res(ParseTypeName(&Range)); + if (!Res.isInvalid()) { Exceptions.push_back(Res.get()); + Ranges.push_back(Range); + } if (Tok.is(tok::comma)) ConsumeToken(); else diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 5c063868bb..8b8d4e1924 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1481,7 +1481,7 @@ Parser::OwningExprResult Parser::ParseBlockLiteralExpression() { ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false, SourceLocation(), 0, 0, 0, - false, false, 0, 0, + false, false, 0, 0, 0, CaretLoc, ParamInfo), CaretLoc); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 6559ca4f8f..6214eb5a11 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -349,6 +349,7 @@ public: QualType GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip = 0, TagDecl **OwnedDecl = 0); DeclarationName GetNameForDeclarator(Declarator &D); + bool CheckSpecifiedExceptionType(QualType T, const SourceRange &Range); bool CheckDistantExceptionSpec(QualType T); QualType ObjCGetTypeForMethodDefinition(DeclPtrTy D); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index abae9f04cc..8375a68507 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3176,7 +3176,7 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, assert(!Error && "Error setting up implicit decl!"); Declarator D(DS, Declarator::BlockContext); D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, SourceLocation(), 0, - 0, 0, false, false, 0, 0, Loc, D), + 0, 0, false, false, 0,0,0, Loc, D), SourceLocation()); D.SetIdentifier(&II, Loc); diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 01b8aa4fd4..833620b5cf 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -760,13 +760,17 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip, // function takes no arguments. llvm::SmallVector Exceptions; Exceptions.reserve(FTI.NumExceptions); - for(unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) - Exceptions.push_back( - QualType::getFromOpaquePtr(FTI.Exceptions[ei])); + for(unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) { + QualType ET = QualType::getFromOpaquePtr(FTI.Exceptions[ei].Ty); + // Check that the type is valid for an exception spec, and drop it + // if not. + if (!CheckSpecifiedExceptionType(ET, FTI.Exceptions[ei].Range)) + Exceptions.push_back(ET); + } T = Context.getFunctionType(T, NULL, 0, FTI.isVariadic, FTI.TypeQuals, FTI.hasExceptionSpec, FTI.hasAnyExceptionSpec, - FTI.NumExceptions, Exceptions.data()); + Exceptions.size(), Exceptions.data()); } else if (FTI.isVariadic) { // We allow a zero-parameter variadic function in C if the // function is marked with the "overloadable" @@ -843,14 +847,19 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip, llvm::SmallVector Exceptions; Exceptions.reserve(FTI.NumExceptions); - for(unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) - Exceptions.push_back(QualType::getFromOpaquePtr(FTI.Exceptions[ei])); + for(unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) { + QualType ET = QualType::getFromOpaquePtr(FTI.Exceptions[ei].Ty); + // Check that the type is valid for an exception spec, and drop it if + // not. + if (!CheckSpecifiedExceptionType(ET, FTI.Exceptions[ei].Range)) + Exceptions.push_back(ET); + } T = Context.getFunctionType(T, ArgTys.data(), ArgTys.size(), FTI.isVariadic, FTI.TypeQuals, FTI.hasExceptionSpec, FTI.hasAnyExceptionSpec, - FTI.NumExceptions, Exceptions.data()); + Exceptions.size(), Exceptions.data()); } break; } @@ -953,6 +962,43 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip, return T; } +/// CheckSpecifiedExceptionType - Check if the given type is valid in an +/// exception specification. Incomplete types, or pointers to incomplete types +/// other than void are not allowed. +bool Sema::CheckSpecifiedExceptionType(QualType T, const SourceRange &Range) { + // FIXME: This may not correctly work with the fix for core issue 437, + // where a class's own type is considered complete within its body. + + // C++ 15.4p2: A type denoted in an exception-specification shall not denote + // an incomplete type. + if (T->isIncompleteType()) + return Diag(Range.getBegin(), diag::err_incomplete_in_exception_spec) + << Range << T << /*direct*/0; + + // C++ 15.4p2: A type denoted in an exception-specification shall not denote + // an incomplete type a pointer or reference to an incomplete type, other + // than (cv) void*. + // The standard does not mention member pointers, but it has to mean them too. + int kind; + if (const PointerType* IT = T->getAsPointerType()) { + T = IT->getPointeeType(); + kind = 1; + } else if (const MemberPointerType* IT = T->getAsMemberPointerType()) { + T = IT->getPointeeType(); + kind = 2; + } else if (const ReferenceType* IT = T->getAsReferenceType()) { + T = IT->getPointeeType(); + kind = 3; + } else + return false; + + if (T->isIncompleteType() && !T->isVoidType()) + return Diag(Range.getBegin(), diag::err_incomplete_in_exception_spec) + << Range << T << /*indirect*/kind; + + return false; +} + /// CheckDistantExceptionSpec - Check if the given type is a pointer or pointer /// to member to a function with an exception specification. This means that /// it is invalid to add another level of indirection. diff --git a/test/SemaCXX/exception-spec.cpp b/test/SemaCXX/exception-spec.cpp index d697e02355..a797dba1d2 100644 --- a/test/SemaCXX/exception-spec.cpp +++ b/test/SemaCXX/exception-spec.cpp @@ -23,3 +23,13 @@ void (**k)(void pfa() throw(int)); // no-error void (**j)() throw(int); // expected-error {{not allowed beyond a single}} // Pointer to function returning pointer to pointer to function with spec void (**(*h())())() throw(int); // expected-error {{not allowed beyond a single}} + +struct Incomplete; + +// Exception spec must not have incomplete types, or pointers to them, except +// void. +void ic1() throw(void); // expected-error {{incomplete type 'void' is not allowed in exception specification}} +void ic2() throw(Incomplete); // expected-error {{incomplete type 'struct Incomplete' is not allowed in exception specification}} +void ic3() throw(void*); +void ic4() throw(Incomplete*); // expected-error {{pointer to incomplete type 'struct Incomplete' is not allowed in exception specification}} +void ic5() throw(Incomplete&); // expected-error {{reference to incomplete type 'struct Incomplete' is not allowed in exception specification}} -- 2.40.0