Implement checking of exception spec compatibility for overriding virtual functions.

llvm-svn: 74943
This commit is contained in:
Sebastian Redl 2009-07-07 20:29:57 +00:00
parent e9f9804762
commit 86be854fa8
6 changed files with 169 additions and 6 deletions

View File

@ -289,6 +289,9 @@ def err_incomplete_in_exception_spec : Error<
"in exception specification">;
def err_mismatched_exception_spec : Error<
"exception specification in declaration does not match previous declaration">;
def err_override_exception_spec : Error<
"exception specification of overriding function is more lax than "
"base version">;
// C++ access checking
def err_class_redeclared_with_different_access : Error<

View File

@ -398,6 +398,9 @@ public:
bool CheckEquivalentExceptionSpec(
const FunctionProtoType *Old, SourceLocation OldLoc,
const FunctionProtoType *New, SourceLocation NewLoc);
bool CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID,
const FunctionProtoType *Superset, SourceLocation SuperLoc,
const FunctionProtoType *Subset, SourceLocation SubLoc);
QualType ObjCGetTypeForMethodDefinition(DeclPtrTy D);
@ -1981,11 +1984,15 @@ public:
std::string getAmbiguousPathsDisplayString(BasePaths &Paths);
/// CheckReturnTypeCovariance - Checks whether two types are covariant,
/// according to C++ [class.virtual]p5.
bool CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
/// CheckOverridingFunctionReturnType - Checks whether the return types are
/// covariant, according to C++ [class.virtual]p5.
bool CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
const CXXMethodDecl *Old);
/// CheckOverridingFunctionExceptionSpec - Checks whether the exception
/// spec is a subset of base spec.
bool CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
const CXXMethodDecl *Old);
//===--------------------------------------------------------------------===//
// C++ Access Control

View File

@ -2244,7 +2244,8 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
for (BasePaths::decl_iterator I = Paths.found_decls_begin(),
E = Paths.found_decls_end(); I != E; ++I) {
if (CXXMethodDecl *OldMD = dyn_cast<CXXMethodDecl>(*I)) {
if (!CheckOverridingFunctionReturnType(NewMD, OldMD))
if (!CheckOverridingFunctionReturnType(NewMD, OldMD) &&
!CheckOverridingFunctionExceptionSpec(NewMD, OldMD))
NewMD->addOverriddenMethod(OldMD);
}
}

View File

@ -882,7 +882,7 @@ namespace {
i != e; ++i) {
// Traverse the record, looking for methods.
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(*i)) {
// If the method is pre virtual, add it to the methods vector.
// If the method is pure virtual, add it to the methods vector.
if (MD->isPure()) {
Methods.push_back(MD);
continue;
@ -3223,6 +3223,17 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
return false;
}
bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
const CXXMethodDecl *Old)
{
return CheckExceptionSpecSubset(diag::err_override_exception_spec,
diag::note_overridden_virtual_function,
Old->getType()->getAsFunctionProtoType(),
Old->getLocation(),
New->getType()->getAsFunctionProtoType(),
New->getLocation());
}
/// ActOnCXXEnterDeclInitializer - Invoked when we are about to parse an
/// initializer for the declaration 'Dcl'.
/// After this method is called, according to [C++ 3.4.1p13], if 'Dcl' is a

View File

@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "Sema.h"
#include "SemaInherit.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
@ -1237,6 +1238,95 @@ bool Sema::CheckEquivalentExceptionSpec(
return true;
}
/// CheckExceptionSpecSubset - Check whether the second function type's
/// exception specification is a subset (or equivalent) of the first function
/// type. This is used by override and pointer assignment checks.
bool Sema::CheckExceptionSpecSubset(unsigned DiagID, unsigned NoteID,
const FunctionProtoType *Superset, SourceLocation SuperLoc,
const FunctionProtoType *Subset, SourceLocation SubLoc)
{
// FIXME: As usual, we could be more specific in our error messages, but
// that better waits until we've got types with source locations.
// If superset contains everything, we're done.
if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec())
return false;
// It does not. If the subset contains everything, we've failed.
if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) {
Diag(SubLoc, DiagID);
Diag(SuperLoc, NoteID);
return true;
}
// Neither contains everything. Do a proper comparison.
for (FunctionProtoType::exception_iterator SubI = Subset->exception_begin(),
SubE = Subset->exception_end(); SubI != SubE; ++SubI) {
// Take one type from the subset.
QualType CanonicalSubT = Context.getCanonicalType(*SubI);
bool SubIsPointer = false;
if (const ReferenceType *RefTy = CanonicalSubT->getAsReferenceType())
CanonicalSubT = RefTy->getPointeeType();
if (const PointerType *PtrTy = CanonicalSubT->getAsPointerType()) {
CanonicalSubT = PtrTy->getPointeeType();
SubIsPointer = true;
}
bool SubIsClass = CanonicalSubT->isRecordType();
CanonicalSubT.setCVRQualifiers(0);
BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
/*DetectVirtual=*/false);
bool Contained = false;
// Make sure it's in the superset.
for (FunctionProtoType::exception_iterator SuperI =
Superset->exception_begin(), SuperE = Superset->exception_end();
SuperI != SuperE; ++SuperI) {
QualType CanonicalSuperT = Context.getCanonicalType(*SuperI);
// SubT must be SuperT or derived from it, or pointer or reference to
// such types.
if (const ReferenceType *RefTy = CanonicalSuperT->getAsReferenceType())
CanonicalSuperT = RefTy->getPointeeType();
if (SubIsPointer) {
if (const PointerType *PtrTy = CanonicalSuperT->getAsPointerType())
CanonicalSuperT = PtrTy->getPointeeType();
else {
continue;
}
}
CanonicalSuperT.setCVRQualifiers(0);
// If the types are the same, move on to the next type in the subset.
if (CanonicalSubT == CanonicalSuperT) {
Contained = true;
break;
}
// Otherwise we need to check the inheritance.
if (!SubIsClass || !CanonicalSuperT->isRecordType())
continue;
Paths.clear();
if (!IsDerivedFrom(CanonicalSubT, CanonicalSuperT, Paths))
continue;
if (Paths.isAmbiguous(CanonicalSuperT))
continue;
// FIXME: Check base access. Don't forget to enable path recording.
Contained = true;
break;
}
if (!Contained) {
Diag(SubLoc, DiagID);
Diag(SuperLoc, NoteID);
return true;
}
}
// We've run the gauntlet.
return false;
}
/// ObjCGetTypeForMethodDefinition - Builds the type for a method definition
/// declarator
QualType Sema::ObjCGetTypeForMethodDefinition(DeclPtrTy D) {

View File

@ -61,3 +61,54 @@ void r7() throw(float); // expected-error {{exception specification in declarati
// Top-level const doesn't matter.
void r8() throw(int);
void r8() throw(const int);
struct A
{
};
struct B1 : A
{
};
struct B2 : A
{
};
struct D : B1, B2
{
};
struct Base
{
virtual void f1() throw();
virtual void f2();
virtual void f3() throw(...);
virtual void f4() throw(int, float);
virtual void f5() throw(int, float);
virtual void f6() throw(A);
virtual void f7() throw(A, int, float);
virtual void f8();
virtual void g1() throw(); // expected-note {{overridden virtual function is here}}
virtual void g2() throw(int); // expected-note {{overridden virtual function is here}}
virtual void g3() throw(A); // expected-note {{overridden virtual function is here}}
virtual void g4() throw(B1); // expected-note {{overridden virtual function is here}}
};
struct Derived : Base
{
virtual void f1() throw();
virtual void f2() throw(...);
virtual void f3();
virtual void f4() throw(float, int);
virtual void f5() throw(float);
virtual void f6() throw(B1);
virtual void f7() throw(B1, B2, int);
virtual void f8() throw(B2, B2, int, float, char, double, bool);
virtual void g1() throw(int); // expected-error {{exception specification of overriding function is more lax}}
virtual void g2(); // expected-error {{exception specification of overriding function is more lax}}
virtual void g3() throw(D); // expected-error {{exception specification of overriding function is more lax}}
virtual void g4() throw(A); // expected-error {{exception specification of overriding function is more lax}}
};