Reject incomplete types in exception specs.

llvm-svn: 72580
This commit is contained in:
Sebastian Redl 2009-05-29 18:02:33 +00:00
parent 9d809cad50
commit d643456d45
11 changed files with 122 additions and 39 deletions

View File

@ -272,6 +272,9 @@ def err_deleted_decl_not_first : Error<
def err_distant_exception_spec : Error< def err_distant_exception_spec : Error<
"exception specifications are not allowed beyond a single level " "exception specifications are not allowed beyond a single level "
"of indirection">; "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 // C++ access checking
def err_class_redeclared_with_different_access : Error< def err_class_redeclared_with_different_access : Error<

View File

@ -519,6 +519,11 @@ struct DeclaratorChunk {
DefaultArgTokens(DefArgTokens) {} DefaultArgTokens(DefArgTokens) {}
}; };
struct TypeAndRange {
ActionBase::TypeTy *Ty;
SourceRange Range;
};
struct FunctionTypeInfo { struct FunctionTypeInfo {
/// hasPrototype - This is true if the function had at least one typed /// 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, /// 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. /// there are no arguments specified.
ParamInfo *ArgInfo; ParamInfo *ArgInfo;
/// Exceptions - This is a pointer to a new[]'d array of TypeTy pointers /// Exceptions - This is a pointer to a new[]'d array of TypeAndRange
/// that contains the types in the function's exception specification. /// objects that contain the types in the function's exception
ActionBase::TypeTy **Exceptions; /// specification and their locations.
TypeAndRange *Exceptions;
/// freeArgs - reset the argument list to having zero arguments. This is /// freeArgs - reset the argument list to having zero arguments. This is
/// used in various places for error recovery. /// used in various places for error recovery.
@ -700,6 +706,7 @@ struct DeclaratorChunk {
unsigned TypeQuals, bool hasExceptionSpec, unsigned TypeQuals, bool hasExceptionSpec,
bool hasAnyExceptionSpec, bool hasAnyExceptionSpec,
ActionBase::TypeTy **Exceptions, ActionBase::TypeTy **Exceptions,
SourceRange *ExceptionRanges,
unsigned NumExceptions, SourceLocation Loc, unsigned NumExceptions, SourceLocation Loc,
Declarator &TheDeclarator); Declarator &TheDeclarator);

View File

@ -772,7 +772,8 @@ private:
OwningExprResult ParseThrowExpression(); OwningExprResult ParseThrowExpression();
// EndLoc is filled with the location of the last token of the specification. // EndLoc is filled with the location of the last token of the specification.
bool ParseExceptionSpecification(SourceLocation &EndLoc, bool ParseExceptionSpecification(SourceLocation &EndLoc,
std::vector<TypeTy*> &Exceptions, llvm::SmallVector<TypeTy*, 2> &Exceptions,
llvm::SmallVector<SourceRange, 2> &Ranges,
bool &hasAnyExceptionSpec); bool &hasAnyExceptionSpec);
//===--------------------------------------------------------------------===// //===--------------------------------------------------------------------===//
@ -1057,7 +1058,7 @@ private:
TPResult TryParseFunctionDeclarator(); TPResult TryParseFunctionDeclarator();
TPResult TryParseBracketDeclarator(); TPResult TryParseBracketDeclarator();
TypeResult ParseTypeName(); TypeResult ParseTypeName(SourceRange *Range = 0);
void ParseBlockId(); void ParseBlockId();
// EndLoc, if non-NULL, is filled with the location of the last token of // EndLoc, if non-NULL, is filled with the location of the last token of
// the attribute list. // the attribute list.

View File

@ -35,6 +35,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
bool hasExceptionSpec, bool hasExceptionSpec,
bool hasAnyExceptionSpec, bool hasAnyExceptionSpec,
ActionBase::TypeTy **Exceptions, ActionBase::TypeTy **Exceptions,
SourceRange *ExceptionRanges,
unsigned NumExceptions, unsigned NumExceptions,
SourceLocation Loc, SourceLocation Loc,
Declarator &TheDeclarator) { Declarator &TheDeclarator) {
@ -72,9 +73,11 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
} }
// new[] an exception array if needed // new[] an exception array if needed
if (NumExceptions) { if (NumExceptions) {
I.Fun.Exceptions = new ActionBase::TypeTy*[NumExceptions]; I.Fun.Exceptions = new DeclaratorChunk::TypeAndRange[NumExceptions];
memcpy(I.Fun.Exceptions, Exceptions, for (unsigned i = 0; i != NumExceptions; ++i) {
sizeof(ActionBase::TypeTy*)*NumExceptions); I.Fun.Exceptions[i].Ty = Exceptions[i];
I.Fun.Exceptions[i].Range = ExceptionRanges[i];
}
} }
return I; return I;
} }

View File

@ -27,7 +27,7 @@ using namespace clang;
/// specifier-qualifier-list abstract-declarator[opt] /// specifier-qualifier-list abstract-declarator[opt]
/// ///
/// Called type-id in C++. /// Called type-id in C++.
Action::TypeResult Parser::ParseTypeName() { Action::TypeResult Parser::ParseTypeName(SourceRange *Range) {
// Parse the common declaration-specifiers piece. // Parse the common declaration-specifiers piece.
DeclSpec DS; DeclSpec DS;
ParseSpecifierQualifierList(DS); ParseSpecifierQualifierList(DS);
@ -35,6 +35,8 @@ Action::TypeResult Parser::ParseTypeName() {
// Parse the abstract-declarator, if present. // Parse the abstract-declarator, if present.
Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); Declarator DeclaratorInfo(DS, Declarator::TypeNameContext);
ParseDeclarator(DeclaratorInfo); ParseDeclarator(DeclaratorInfo);
if (Range)
*Range = DeclaratorInfo.getSourceRange();
if (DeclaratorInfo.isInvalidType()) if (DeclaratorInfo.isInvalidType())
return true; return true;
@ -2273,9 +2275,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
DeclSpec DS; DeclSpec DS;
bool hasExceptionSpec = false; bool hasExceptionSpec = false;
bool hasAnyExceptionSpec = false; bool hasAnyExceptionSpec = false;
// FIXME: Does an empty vector ever allocate? Exception specifications are llvm::SmallVector<TypeTy*, 2> Exceptions;
// extremely rare, so we want something like a SmallVector<TypeTy*, 0>. :-) llvm::SmallVector<SourceRange, 2> ExceptionRanges;
std::vector<TypeTy*> Exceptions;
if (getLang().CPlusPlus) { if (getLang().CPlusPlus) {
ParseTypeQualifierListOpt(DS, false /*no attributes*/); ParseTypeQualifierListOpt(DS, false /*no attributes*/);
if (!DS.getSourceRange().getEnd().isInvalid()) if (!DS.getSourceRange().getEnd().isInvalid())
@ -2284,7 +2285,10 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
// Parse exception-specification[opt]. // Parse exception-specification[opt].
if (Tok.is(tok::kw_throw)) { if (Tok.is(tok::kw_throw)) {
hasExceptionSpec = true; 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,8 +2301,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
DS.getTypeQualifiers(), DS.getTypeQualifiers(),
hasExceptionSpec, hasExceptionSpec,
hasAnyExceptionSpec, hasAnyExceptionSpec,
Exceptions.empty() ? 0 : Exceptions.data(),
&Exceptions[0], ExceptionRanges.data(),
Exceptions.size(), Exceptions.size(),
LParenLoc, D), LParenLoc, D),
Loc); Loc);
@ -2445,9 +2449,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
DeclSpec DS; DeclSpec DS;
bool hasExceptionSpec = false; bool hasExceptionSpec = false;
bool hasAnyExceptionSpec = false; bool hasAnyExceptionSpec = false;
// FIXME: Does an empty vector ever allocate? Exception specifications are llvm::SmallVector<TypeTy*, 2> Exceptions;
// extremely rare, so we want something like a SmallVector<TypeTy*, 0>. :-) llvm::SmallVector<SourceRange, 2> ExceptionRanges;
std::vector<TypeTy*> Exceptions;
if (getLang().CPlusPlus) { if (getLang().CPlusPlus) {
// Parse cv-qualifier-seq[opt]. // Parse cv-qualifier-seq[opt].
ParseTypeQualifierListOpt(DS, false /*no attributes*/); ParseTypeQualifierListOpt(DS, false /*no attributes*/);
@ -2457,7 +2460,10 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
// Parse exception-specification[opt]. // Parse exception-specification[opt].
if (Tok.is(tok::kw_throw)) { if (Tok.is(tok::kw_throw)) {
hasExceptionSpec = true; 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(), DS.getTypeQualifiers(),
hasExceptionSpec, hasExceptionSpec,
hasAnyExceptionSpec, hasAnyExceptionSpec,
Exceptions.empty() ? 0 : Exceptions.data(),
&Exceptions[0], ExceptionRanges.data(),
Exceptions.size(), LParenLoc, D), Exceptions.size(), LParenLoc, D),
Loc); Loc);
} }
@ -2545,7 +2551,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList(SourceLocation LParenLoc,
SourceLocation(), SourceLocation(),
&ParamInfo[0], ParamInfo.size(), &ParamInfo[0], ParamInfo.size(),
/*TypeQuals*/0, /*TypeQuals*/0,
/*exception*/false, false, 0, 0, /*exception*/false, false, 0, 0, 0,
LParenLoc, D), LParenLoc, D),
RLoc); RLoc);
} }

View File

@ -1191,7 +1191,10 @@ Parser::MemInitResult Parser::ParseMemInitializer(DeclPtrTy ConstructorDecl) {
/// type-id-list ',' type-id /// type-id-list ',' type-id
/// ///
bool Parser::ParseExceptionSpecification(SourceLocation &EndLoc, bool Parser::ParseExceptionSpecification(SourceLocation &EndLoc,
std::vector<TypeTy*> &Exceptions, llvm::SmallVector<TypeTy*, 2>
&Exceptions,
llvm::SmallVector<SourceRange, 2>
&Ranges,
bool &hasAnyExceptionSpec) { bool &hasAnyExceptionSpec) {
assert(Tok.is(tok::kw_throw) && "expected throw"); assert(Tok.is(tok::kw_throw) && "expected throw");
@ -1214,10 +1217,13 @@ bool Parser::ParseExceptionSpecification(SourceLocation &EndLoc,
} }
// Parse the sequence of type-ids. // Parse the sequence of type-ids.
SourceRange Range;
while (Tok.isNot(tok::r_paren)) { while (Tok.isNot(tok::r_paren)) {
TypeResult Res(ParseTypeName()); TypeResult Res(ParseTypeName(&Range));
if (!Res.isInvalid()) if (!Res.isInvalid()) {
Exceptions.push_back(Res.get()); Exceptions.push_back(Res.get());
Ranges.push_back(Range);
}
if (Tok.is(tok::comma)) if (Tok.is(tok::comma))
ConsumeToken(); ConsumeToken();
else else

View File

@ -1481,7 +1481,7 @@ Parser::OwningExprResult Parser::ParseBlockLiteralExpression() {
ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false, ParamInfo.AddTypeInfo(DeclaratorChunk::getFunction(true, false,
SourceLocation(), SourceLocation(),
0, 0, 0, 0, 0, 0,
false, false, 0, 0, false, false, 0, 0, 0,
CaretLoc, ParamInfo), CaretLoc, ParamInfo),
CaretLoc); CaretLoc);

View File

@ -349,6 +349,7 @@ public:
QualType GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip = 0, QualType GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip = 0,
TagDecl **OwnedDecl = 0); TagDecl **OwnedDecl = 0);
DeclarationName GetNameForDeclarator(Declarator &D); DeclarationName GetNameForDeclarator(Declarator &D);
bool CheckSpecifiedExceptionType(QualType T, const SourceRange &Range);
bool CheckDistantExceptionSpec(QualType T); bool CheckDistantExceptionSpec(QualType T);
QualType ObjCGetTypeForMethodDefinition(DeclPtrTy D); QualType ObjCGetTypeForMethodDefinition(DeclPtrTy D);

View File

@ -3176,7 +3176,7 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc,
assert(!Error && "Error setting up implicit decl!"); assert(!Error && "Error setting up implicit decl!");
Declarator D(DS, Declarator::BlockContext); Declarator D(DS, Declarator::BlockContext);
D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, SourceLocation(), 0, 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()); SourceLocation());
D.SetIdentifier(&II, Loc); D.SetIdentifier(&II, Loc);

View File

@ -760,13 +760,17 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip,
// function takes no arguments. // function takes no arguments.
llvm::SmallVector<QualType, 4> Exceptions; llvm::SmallVector<QualType, 4> Exceptions;
Exceptions.reserve(FTI.NumExceptions); Exceptions.reserve(FTI.NumExceptions);
for(unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) for(unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) {
Exceptions.push_back( QualType ET = QualType::getFromOpaquePtr(FTI.Exceptions[ei].Ty);
QualType::getFromOpaquePtr(FTI.Exceptions[ei])); // 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, T = Context.getFunctionType(T, NULL, 0, FTI.isVariadic, FTI.TypeQuals,
FTI.hasExceptionSpec, FTI.hasExceptionSpec,
FTI.hasAnyExceptionSpec, FTI.hasAnyExceptionSpec,
FTI.NumExceptions, Exceptions.data()); Exceptions.size(), Exceptions.data());
} else if (FTI.isVariadic) { } else if (FTI.isVariadic) {
// We allow a zero-parameter variadic function in C if the // We allow a zero-parameter variadic function in C if the
// function is marked with the "overloadable" // function is marked with the "overloadable"
@ -843,14 +847,19 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip,
llvm::SmallVector<QualType, 4> Exceptions; llvm::SmallVector<QualType, 4> Exceptions;
Exceptions.reserve(FTI.NumExceptions); Exceptions.reserve(FTI.NumExceptions);
for(unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) for(unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) {
Exceptions.push_back(QualType::getFromOpaquePtr(FTI.Exceptions[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(), T = Context.getFunctionType(T, ArgTys.data(), ArgTys.size(),
FTI.isVariadic, FTI.TypeQuals, FTI.isVariadic, FTI.TypeQuals,
FTI.hasExceptionSpec, FTI.hasExceptionSpec,
FTI.hasAnyExceptionSpec, FTI.hasAnyExceptionSpec,
FTI.NumExceptions, Exceptions.data()); Exceptions.size(), Exceptions.data());
} }
break; break;
} }
@ -953,6 +962,43 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip,
return T; 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 /// CheckDistantExceptionSpec - Check if the given type is a pointer or pointer
/// to member to a function with an exception specification. This means that /// to member to a function with an exception specification. This means that
/// it is invalid to add another level of indirection. /// it is invalid to add another level of indirection.

View File

@ -23,3 +23,13 @@ void (**k)(void pfa() throw(int)); // no-error
void (**j)() throw(int); // expected-error {{not allowed beyond a single}} void (**j)() throw(int); // expected-error {{not allowed beyond a single}}
// Pointer to function returning pointer to pointer to function with spec // Pointer to function returning pointer to pointer to function with spec
void (**(*h())())() throw(int); // expected-error {{not allowed beyond a single}} 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}}