diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 7 | ||||
-rw-r--r-- | lib/AST/CXXInheritance.cpp | 16 | ||||
-rw-r--r-- | lib/Sema/SemaCXXScopeSpec.cpp | 12 | ||||
-rw-r--r-- | lib/Sema/SemaExprMember.cpp | 62 | ||||
-rw-r--r-- | test/CXX/class.derived/class.virtual/p3-0x.cpp | 30 | ||||
-rw-r--r-- | test/CXX/temp/temp.res/temp.dep/p3.cpp | 38 |
6 files changed, 116 insertions, 49 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 15e2e3b558..91a26d885e 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -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, diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp index 213b214a4e..292b23dcc6 100644 --- a/lib/AST/CXXInheritance.cpp +++ b/lib/AST/CXXInheritance.cpp @@ -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); } } - diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index 15bfd1ce62..7db604d9cc 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 @@ 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(); diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index a7fd47183a..6bf5f2e3a2 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 @@ 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; } diff --git a/test/CXX/class.derived/class.virtual/p3-0x.cpp b/test/CXX/class.derived/class.virtual/p3-0x.cpp index 16f98280ed..6a02a86731 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 @@ 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; +} diff --git a/test/CXX/temp/temp.res/temp.dep/p3.cpp b/test/CXX/temp/temp.res/temp.dep/p3.cpp index 88b4752e6b..576e310985 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 @@ 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}} +} |