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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@168477 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h
index 15e2e3b..91a26d8 100644
--- a/include/clang/AST/DeclCXX.h
+++ b/include/clang/AST/DeclCXX.h
@@ -765,7 +765,8 @@
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 @@
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,
diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp
index 213b214..292b23d 100644
--- a/lib/AST/CXXInheritance.cpp
+++ b/lib/AST/CXXInheritance.cpp
@@ -121,6 +121,17 @@
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 @@
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 @@
AddIndirectPrimaryBases(BaseDecl, Context, Bases);
}
}
-
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index 15bfd1c..7db604d 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -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 @@
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();
diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp
index a7fd471..6bf5f2e 100644
--- a/lib/Sema/SemaExprMember.cpp
+++ b/lib/Sema/SemaExprMember.cpp
@@ -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 @@
// 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 @@
// 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 @@
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 @@
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;
}
diff --git a/test/CXX/class.derived/class.virtual/p3-0x.cpp b/test/CXX/class.derived/class.virtual/p3-0x.cpp
index 16f9828..6a02a86 100644
--- a/test/CXX/class.derived/class.virtual/p3-0x.cpp
+++ b/test/CXX/class.derived/class.virtual/p3-0x.cpp
@@ -100,3 +100,33 @@
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;
+}
diff --git a/test/CXX/temp/temp.res/temp.dep/p3.cpp b/test/CXX/temp/temp.res/temp.dep/p3.cpp
index 88b4752..576e310 100644
--- a/test/CXX/temp/temp.res/temp.dep/p3.cpp
+++ b/test/CXX/temp/temp.res/temp.dep/p3.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
-// expected-no-diagnostics
struct A0 {
struct K { };
};
@@ -42,3 +41,40 @@
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}}
+}