diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 3 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 13 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 1 | ||||
-rw-r--r-- | lib/Sema/SemaCXXScopeSpec.cpp | 22 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 12 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 18 | ||||
-rw-r--r-- | test/SemaTemplate/dependent-base-classes.cpp | 53 |
7 files changed, 118 insertions, 4 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 02581c1241..336a895de8 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -393,6 +393,9 @@ public: return reverse_base_class_const_iterator(vbases_begin()); } + /// \brief Determine whether this class has any dependent base classes. + bool hasAnyDependentBases() const; + /// Iterator access to method members. The method iterator visits /// all method members of the class, including non-instance methods, /// special methods, etc. diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index b30d335918..1cce35c0b3 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -144,6 +144,19 @@ CXXRecordDecl::setBases(ASTContext &C, } } +/// Callback function for CXXRecordDecl::forallBases that acknowledges +/// that it saw a base class. +static bool SawBase(const CXXRecordDecl *, void *) { + return true; +} + +bool CXXRecordDecl::hasAnyDependentBases() const { + if (!isDependentContext()) + return false; + + return !forallBases(SawBase, 0); +} + bool CXXRecordDecl::hasConstCopyConstructor(ASTContext &Context) const { return getCopyConstructor(Context, Qualifiers::Const) != 0; } diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 8fc34880c1..44bf48915e 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2032,6 +2032,7 @@ public: bool isDependentScopeSpecifier(const CXXScopeSpec &SS); CXXRecordDecl *getCurrentInstantiationOf(NestedNameSpecifier *NNS); bool isUnknownSpecialization(const CXXScopeSpec &SS); + bool isCurrentInstantiationWithDependentBases(const CXXScopeSpec &SS); /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the /// global scope ('::'). diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index a8f118ebdd..8594583ec3 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -210,6 +210,28 @@ bool Sema::isUnknownSpecialization(const CXXScopeSpec &SS) { return getCurrentInstantiationOf(NNS) == 0; } +/// \brief Determine whether the given scope specifier refers to a +/// current instantiation that has any dependent base clases. +/// +/// This check is typically used when we've performed lookup into the +/// current instantiation of a template, but that lookup failed. When +/// there are dependent bases present, however, the lookup needs to be +/// delayed until template instantiation time. +bool Sema::isCurrentInstantiationWithDependentBases(const CXXScopeSpec &SS) { + if (!SS.isSet()) + return false; + + NestedNameSpecifier *NNS = (NestedNameSpecifier*)SS.getScopeRep(); + if (!NNS->isDependent()) + return false; + + CXXRecordDecl *CurrentInstantiation = getCurrentInstantiationOf(NNS); + if (!CurrentInstantiation) + return false; + + return CurrentInstantiation->hasAnyDependentBases(); +} + /// \brief If the given nested name specifier refers to the current /// instantiation, return the declaration that corresponds to that /// current instantiation (C++0x [temp.dep.type]p1). diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 75fe7fd00f..c54745f552 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -4785,8 +4785,18 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, if (Previous.isAmbiguous()) return DeclPtrTy(); - // A tag 'foo::bar' must already exist. if (Previous.empty()) { + // Name lookup did not find anything. However, if the + // nested-name-specifier refers to the current instantiation, + // and that current instantiation has any dependent base + // classes, we might find something at instantiation time: treat + // this as a dependent elaborated-type-specifier. + if (isCurrentInstantiationWithDependentBases(SS)) { + IsDependent = true; + return DeclPtrTy(); + } + + // A tag 'foo::bar' must already exist. Diag(NameLoc, diag::err_not_tag_in_scope) << Name << SS.getRange(); Name = 0; Invalid = true; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index e75277e823..d2e70cf9ad 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1608,15 +1608,19 @@ Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc, TemplateTy Template; TemplateNameKind TNK = isTemplateName(0, SS, Name, ObjectType, EnteringContext, Template); - if (TNK == TNK_Non_template) { + if (TNK == TNK_Non_template && + isCurrentInstantiationWithDependentBases(SS)) { + // This is a dependent template. + } else if (TNK == TNK_Non_template) { Diag(Name.getSourceRange().getBegin(), diag::err_template_kw_refers_to_non_template) << GetNameFromUnqualifiedId(Name) << Name.getSourceRange(); return TemplateTy(); + } else { + // We found something; return it. + return Template; } - - return Template; } NestedNameSpecifier *Qualifier @@ -4765,6 +4769,14 @@ Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II, Decl *Referenced = 0; switch (Result.getResultKind()) { case LookupResult::NotFound: + if (CurrentInstantiation && CurrentInstantiation->hasAnyDependentBases()) { + // We performed a lookup in the current instantiation and didn't + // find anything. However, this current instantiation has + // dependent bases, so we might be able to find something at + // instantiation time: just build a TypenameType and move on. + return Context.getTypenameType(NNS, &II); + } + DiagID = diag::err_typename_nested_not_found; break; diff --git a/test/SemaTemplate/dependent-base-classes.cpp b/test/SemaTemplate/dependent-base-classes.cpp index 242765894f..b9666dba61 100644 --- a/test/SemaTemplate/dependent-base-classes.cpp +++ b/test/SemaTemplate/dependent-base-classes.cpp @@ -10,3 +10,56 @@ struct X1 : T::apply<U> { }; // expected-error{{missing 'template' keyword prior template<typename T> struct X2 : vector<T> { }; // expected-error{{unknown template name 'vector'}} + +namespace PR6031 { + template<typename T> + struct A; + + template <class X> + struct C { }; + + template <class TT> + struct II { + typedef typename A<TT>::type type; + }; + + template <class TT> + struct FI : II<TT> + { + C<typename FI::type> a; + }; + + template <class TT> + struct FI2 + { + C<typename FI2::type> a; // expected-error{{no type named 'type' in 'struct PR6031::FI2'}} \ + // expected-error{{C++ requires a type specifier for all declarations}} + }; + + template<typename T> + struct Base { + class Nested { }; + template<typename U> struct MemberTemplate { }; + int a; + }; + + template<typename T> + struct HasDepBase : Base<T> { + int foo() { + class HasDepBase::Nested nested; + typedef typename HasDepBase::template MemberTemplate<T>::type type; + return HasDepBase::a; + } + }; + + template<typename T> + struct NoDepBase { + int foo() { + class NoDepBase::Nested nested; // expected-error{{'Nested' does not name a tag member in the specified scope}} + typedef typename NoDepBase::template MemberTemplate<T>::type type; // expected-error{{'MemberTemplate' following the 'template' keyword does not refer to a template}} \ + // FIXME: expected-error{{expected an identifier or template-id after '::'}} \ + // FIXME: expected-error{{unqualified-id}} + return NoDepBase::a; // expected-error{{no member named 'a' in 'struct PR6031::NoDepBase'}} + } + }; +} |