]> granicus.if.org Git - clang/commitdiff
When qualified lookup into the current instantiation fails (because it
authorDouglas Gregor <dgregor@apple.com>
Thu, 14 Jan 2010 17:47:39 +0000 (17:47 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 14 Jan 2010 17:47:39 +0000 (17:47 +0000)
finds nothing), and the current instantiation has dependent base
classes, treat the qualified lookup as if it referred to an unknown
specialization. Fixes PR6031.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@93433 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclCXX.h
lib/AST/DeclCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaCXXScopeSpec.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaTemplate.cpp
test/SemaTemplate/dependent-base-classes.cpp

index 02581c1241a4c4790959436ebc564569d60322f6..336a895de8cfb0737f3a0bb418d85b5e1b2bb2d3 100644 (file)
@@ -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.
index b30d335918ab1e2bc5db94cd178f34bdc39544fd..1cce35c0b39a4c8baf3f19d8850c4b9d4316846a 100644 (file)
@@ -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;
 }
index 8fc34880c1103bef20b054847206e594e0da9665..44bf48915ea7d7e66780583e3d101e6b46e460bd 100644 (file)
@@ -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 ('::').
index a8f118ebdd67271c409b12292f3db179dbcd0047..8594583ec3eff223cbbec9f3b301305a149d1a2e 100644 (file)
@@ -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).
index 75fe7fd00fcf10afe2459ca21142f563b782dabf..c54745f552e10b5ed1ea60e6de3880ada41a4e2d 100644 (file)
@@ -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;
index e75277e823e0ae156cbb05011eeb311fa407a81f..d2e70cf9ad6f0efd64ff1ab0c271318ecff58738 100644 (file)
@@ -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;
 
index 242765894f7341236f544fcd96f6a8f58a9b6ed7..b9666dba61c661251299c7a47834ef4415badd95 100644 (file)
@@ -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'}}
+    }
+  };
+}