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
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,
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 {
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;
AddIndirectPrimaryBases(BaseDecl, Context, Bases);
}
}
-
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;
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();
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 {
// 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;
// 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;
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
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;
}
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;
+}
// RUN: %clang_cc1 -fsyntax-only -verify %s
-// expected-no-diagnostics
struct A0 {
struct K { };
};
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}}
+}