Fix CXXRecordDecl::forallBases to not look through bases which are dependent

and defined within the current instantiation, but which are not part of the
current instantiation. Previously, it would look at bases which could be
specialized separately from the current template.

llvm-svn: 168477
This commit is contained in:
Richard Smith 2012-11-22 00:24:47 +00:00
parent 56cb16dd92
commit d80b2d57cf
6 changed files with 116 additions and 49 deletions

View File

@ -765,7 +765,8 @@ public:
return reverse_base_class_const_iterator(vbases_begin());
}
/// \brief Determine whether this class has any dependent base classes.
/// \brief Determine whether this class has any dependent base classes which
/// are not the current instantiation.
bool hasAnyDependentBases() const;
/// Iterator access to method members. The method iterator visits
@ -1308,6 +1309,10 @@ public:
return dyn_cast<FunctionDecl>(getDeclContext());
}
/// \brief Determine whether this dependent class is a current instantiation,
/// when viewed from within the given context.
bool isCurrentInstantiation(const DeclContext *CurContext) const;
/// \brief Determine whether this class is derived from the class \p Base.
///
/// This routine only determines whether this class is derived from \p Base,

View File

@ -121,6 +121,17 @@ bool CXXRecordDecl::isProvablyNotDerivedFrom(const CXXRecordDecl *Base) const {
return forallBases(BaseIsNot, (void*) Base->getCanonicalDecl());
}
bool
CXXRecordDecl::isCurrentInstantiation(const DeclContext *CurContext) const {
assert(isDependentContext());
for (; !CurContext->isFileContext(); CurContext = CurContext->getParent())
if (CurContext->Equals(this))
return true;
return false;
}
bool CXXRecordDecl::forallBases(ForallBasesCallback *BaseMatches,
void *OpaqueData,
bool AllowShortCircuit) const {
@ -140,7 +151,9 @@ bool CXXRecordDecl::forallBases(ForallBasesCallback *BaseMatches,
CXXRecordDecl *Base =
cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition());
if (!Base) {
if (!Base ||
(Base->isDependentContext() &&
!Base->isCurrentInstantiation(Record))) {
if (AllowShortCircuit) return false;
AllMatches = false;
continue;
@ -725,4 +738,3 @@ CXXRecordDecl::getIndirectPrimaryBases(CXXIndirectPrimaryBaseSet& Bases) const {
AddIndirectPrimaryBases(BaseDecl, Context, Bases);
}
}

View File

@ -26,7 +26,7 @@
using namespace clang;
/// \brief Find the current instantiation that associated with the given type.
static CXXRecordDecl *getCurrentInstantiationOf(QualType T,
static CXXRecordDecl *getCurrentInstantiationOf(QualType T,
DeclContext *CurContext) {
if (T.isNull())
return 0;
@ -34,16 +34,10 @@ static CXXRecordDecl *getCurrentInstantiationOf(QualType T,
const Type *Ty = T->getCanonicalTypeInternal().getTypePtr();
if (const RecordType *RecordTy = dyn_cast<RecordType>(Ty)) {
CXXRecordDecl *Record = cast<CXXRecordDecl>(RecordTy->getDecl());
if (!T->isDependentType())
if (!Record->isDependentContext() ||
Record->isCurrentInstantiation(CurContext))
return Record;
// This may be a member of a class template or class template partial
// specialization. If it's part of the current semantic context, then it's
// an injected-class-name;
for (; !CurContext->isFileContext(); CurContext = CurContext->getParent())
if (CurContext->Equals(Record))
return Record;
return 0;
} else if (isa<InjectedClassNameType>(Ty))
return cast<InjectedClassNameType>(Ty)->getDecl();

View File

@ -24,30 +24,19 @@
using namespace clang;
using namespace sema;
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 4> BaseSet;
static bool BaseIsNotInSet(const CXXRecordDecl *Base, void *BasesPtr) {
const BaseSet &Bases = *reinterpret_cast<const BaseSet*>(BasesPtr);
return !Bases.count(Base->getCanonicalDecl());
}
/// Determines if the given class is provably not derived from all of
/// the prospective base classes.
static bool IsProvablyNotDerivedFrom(Sema &SemaRef,
CXXRecordDecl *Record,
const llvm::SmallPtrSet<CXXRecordDecl*, 4> &Bases) {
if (Bases.count(Record->getCanonicalDecl()))
return false;
RecordDecl *RD = Record->getDefinition();
if (!RD) return false;
Record = cast<CXXRecordDecl>(RD);
for (CXXRecordDecl::base_class_iterator I = Record->bases_begin(),
E = Record->bases_end(); I != E; ++I) {
CanQualType BaseT = SemaRef.Context.getCanonicalType((*I).getType());
CanQual<RecordType> BaseRT = BaseT->getAs<RecordType>();
if (!BaseRT) return false;
CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(BaseRT->getDecl());
if (!IsProvablyNotDerivedFrom(SemaRef, BaseRecord, Bases))
return false;
}
return true;
static bool isProvablyNotDerivedFrom(Sema &SemaRef, CXXRecordDecl *Record,
const BaseSet &Bases) {
void *BasesPtr = const_cast<void*>(reinterpret_cast<const void*>(&Bases));
return BaseIsNotInSet(Record, BasesPtr) &&
Record->forallBases(BaseIsNotInSet, BasesPtr);
}
enum IMAKind {
@ -111,7 +100,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
// Collect all the declaring classes of instance members we find.
bool hasNonInstance = false;
bool isField = false;
llvm::SmallPtrSet<CXXRecordDecl*, 4> Classes;
BaseSet Classes;
for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
NamedDecl *D = *I;
@ -169,16 +158,18 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
// is ill-formed.
if (R.getNamingClass() &&
contextClass->getCanonicalDecl() !=
R.getNamingClass()->getCanonicalDecl() &&
contextClass->isProvablyNotDerivedFrom(R.getNamingClass()))
return hasNonInstance ? IMA_Mixed_Unrelated :
IsCXX11UnevaluatedField ? IMA_Field_Uneval_Context :
IMA_Error_Unrelated;
R.getNamingClass()->getCanonicalDecl()) {
// If the naming class is not the current context, this was a qualified
// member name lookup, and it's sufficient to check that we have the naming
// class as a base class.
Classes.clear();
Classes.insert(R.getNamingClass());
}
// If we can prove that the current context is unrelated to all the
// declaring classes, it can't be an implicit member reference (in
// which case it's an error if any of those members are selected).
if (IsProvablyNotDerivedFrom(SemaRef, contextClass, Classes))
if (isProvablyNotDerivedFrom(SemaRef, contextClass, Classes))
return hasNonInstance ? IMA_Mixed_Unrelated :
IsCXX11UnevaluatedField ? IMA_Field_Uneval_Context :
IMA_Error_Unrelated;
@ -491,14 +482,14 @@ bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr,
QualType BaseType,
const CXXScopeSpec &SS,
const LookupResult &R) {
const RecordType *BaseRT = BaseType->getAs<RecordType>();
if (!BaseRT) {
CXXRecordDecl *BaseRecord =
cast_or_null<CXXRecordDecl>(computeDeclContext(BaseType));
if (!BaseRecord) {
// We can't check this yet because the base type is still
// dependent.
assert(BaseType->isDependentType());
return false;
}
CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(BaseRT->getDecl());
for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
// If this is an implicit member reference and we find a
@ -513,11 +504,10 @@ bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr,
if (!DC->isRecord())
continue;
llvm::SmallPtrSet<CXXRecordDecl*,4> MemberRecord;
MemberRecord.insert(cast<CXXRecordDecl>(DC)->getCanonicalDecl());
if (!IsProvablyNotDerivedFrom(*this, BaseRecord, MemberRecord))
CXXRecordDecl *MemberRecord = cast<CXXRecordDecl>(DC)->getCanonicalDecl();
if (BaseRecord->getCanonicalDecl() == MemberRecord ||
!BaseRecord->isProvablyNotDerivedFrom(MemberRecord))
return false;
}

View File

@ -100,3 +100,33 @@ namespace PR13499 {
Y<X> y;
Z<X> z; // expected-note {{in instantiation of}}
}
namespace MemberOfUnknownSpecialization {
template<typename T> struct A {
struct B {};
struct C : B {
void f() override;
};
};
template<> struct A<int>::B {
virtual void f();
};
// ok
A<int>::C c1;
template<> struct A<char>::B {
void f();
};
// expected-error@-13 {{only virtual member functions can be marked 'override'}}
// expected-note@+1 {{in instantiation of}}
A<char>::C c2;
template<> struct A<double>::B {
virtual void f() final;
};
// expected-error@-20 {{declaration of 'f' overrides a 'final' function}}
// expected-note@-3 {{here}}
// expected-note@+1 {{in instantiation of}}
A<double>::C c3;
}

View File

@ -1,5 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// expected-no-diagnostics
struct A0 {
struct K { };
};
@ -42,3 +41,40 @@ namespace E2 {
Y<A> ya;
}
namespace PR14402 {
template<typename T>
struct A {
typedef int n;
int f();
struct B {};
struct C : B {
// OK, can't be sure whether we derive from A yet.
using A::n;
int g() { return f(); }
};
struct D {
using A::n; // expected-error {{using declaration refers into 'A<T>::', which is not a base class of 'D'}}
int g() { return f(); } // expected-error {{call to non-static member function 'f' of 'A' from nested type 'D'}}
};
struct E { char &f(); };
struct F : E {
// FIXME: Reject this prior to instantiation; f() is known to return int.
char &g() { return f(); }
// expected-error@-1 {{'PR14402::A<int>::f' is not a member of class 'PR14402::A<int>::F'}}
// expected-error@-2 {{non-const lvalue reference to type 'char' cannot bind to a temporary of type 'int'}}
};
};
template<> struct A<int>::B : A<int> {};
A<int>::C::n n = A<int>::C().g();
// 'not a member'
char &r = A<int>::F().g(); // expected-note {{in instantiation of}}
template<> struct A<char>::E : A<char> {};
// 'cannot bind to a temporary'
char &s = A<char>::F().g(); // expected-note {{in instantiation of}}
}