forked from OSchip/llvm-project
Implement checking of exception spec compatibility for overriding virtual functions.
llvm-svn: 74943
This commit is contained in:
parent
e9f9804762
commit
86be854fa8
|
@ -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<
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue