r177957 - Implement special-case name lookup for inheriting constructors: member

Richard Smith richard-llvm at metafoo.co.uk
Mon Mar 25 18:15:19 PDT 2013


Author: rsmith
Date: Mon Mar 25 20:15:19 2013
New Revision: 177957

URL: http://llvm.org/viewvc/llvm-project?rev=177957&view=rev
Log:
Implement special-case name lookup for inheriting constructors: member
using-declarations with names which look constructor-like are interpreted as
constructor names.

Modified:
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=177957&r1=177956&r2=177957&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Mon Mar 25 20:15:19 2013
@@ -1290,7 +1290,8 @@ private:
                                       ParsedType ObjectType,
                                       bool EnteringContext,
                                       bool *MayBePseudoDestructor = 0,
-                                      bool IsTypename = false);
+                                      bool IsTypename = false,
+                                      IdentifierInfo **LastII = 0);
 
   void CheckForLParenAfterColonColon();
 

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=177957&r1=177956&r2=177957&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Mar 25 20:15:19 2013
@@ -3756,6 +3756,10 @@ public:
                                bool AllowExplicit = false,
                                bool IsListInitialization = false);
 
+  ParsedType getInheritingConstructorName(CXXScopeSpec &SS,
+                                          SourceLocation NameLoc,
+                                          IdentifierInfo &Name);
+
   ParsedType getDestructorName(SourceLocation TildeLoc,
                                IdentifierInfo &II, SourceLocation NameLoc,
                                Scope *S, CXXScopeSpec &SS,

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=177957&r1=177956&r2=177957&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Mon Mar 25 20:15:19 2013
@@ -468,7 +468,10 @@ Decl *Parser::ParseUsingDeclaration(unsi
   }
 
   // Parse nested-name-specifier.
-  ParseOptionalCXXScopeSpecifier(SS, ParsedType(), /*EnteringContext=*/false);
+  IdentifierInfo *LastII = 0;
+  ParseOptionalCXXScopeSpecifier(SS, ParsedType(), /*EnteringContext=*/false,
+                                 /*MayBePseudoDtor=*/0, /*IsTypename=*/false,
+                                 /*LastII=*/&LastII);
 
   // Check nested-name specifier.
   if (SS.isInvalid()) {
@@ -476,18 +479,31 @@ Decl *Parser::ParseUsingDeclaration(unsi
     return 0;
   }
 
+  SourceLocation TemplateKWLoc;
+  UnqualifiedId Name;
+
   // Parse the unqualified-id. We allow parsing of both constructor and
   // destructor names and allow the action module to diagnose any semantic
   // errors.
-  SourceLocation TemplateKWLoc;
-  UnqualifiedId Name;
-  if (ParseUnqualifiedId(SS,
-                         /*EnteringContext=*/false,
-                         /*AllowDestructorName=*/true,
-                         /*AllowConstructorName=*/true,
-                         ParsedType(),
-                         TemplateKWLoc,
-                         Name)) {
+  //
+  // C++11 [class.qual]p2:
+  //   [...] in a using-declaration that is a member-declaration, if the name
+  //   specified after the nested-name-specifier is the same as the identifier
+  //   or the simple-template-id's template-name in the last component of the
+  //   nested-name-specifier, the name is [...] considered to name the
+  //   constructor.
+  if (getLangOpts().CPlusPlus11 && Context == Declarator::MemberContext &&
+      Tok.is(tok::identifier) && NextToken().is(tok::semi) &&
+      SS.isNotEmpty() && LastII == Tok.getIdentifierInfo() &&
+      !SS.getScopeRep()->getAsNamespace() &&
+      !SS.getScopeRep()->getAsNamespaceAlias()) {
+    SourceLocation IdLoc = ConsumeToken();
+    ParsedType Type = Actions.getInheritingConstructorName(SS, IdLoc, *LastII);
+    Name.setConstructorName(Type, IdLoc, IdLoc);
+  } else if (ParseUnqualifiedId(SS, /*EnteringContext=*/ false,
+                                /*AllowDestructorName=*/ true,
+                                /*AllowConstructorName=*/ true, ParsedType(),
+                                TemplateKWLoc, Name)) {
     SkipUntil(tok::semi);
     return 0;
   }

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=177957&r1=177956&r2=177957&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Mon Mar 25 20:15:19 2013
@@ -168,19 +168,26 @@ void Parser::CheckForLParenAfterColonCol
 /// if we do end up determining that we are parsing a destructor name,
 /// the last component of the nested-name-specifier is not parsed as
 /// part of the scope specifier.
-
-/// member access expression, e.g., the \p T:: in \p p->T::m.
+///
+/// \param IsTypename If \c true, this nested-name-specifier is known to be
+/// part of a type name. This is used to improve error recovery.
+///
+/// \param LastII When non-NULL, points to an IdentifierInfo* that will be
+/// filled in with the leading identifier in the last component of the
+/// nested-name-specifier, if any.
 ///
 /// \returns true if there was an error parsing a scope specifier
 bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
                                             ParsedType ObjectType,
                                             bool EnteringContext,
                                             bool *MayBePseudoDestructor,
-                                            bool IsTypename) {
+                                            bool IsTypename,
+                                            IdentifierInfo **LastII) {
   assert(getLangOpts().CPlusPlus &&
          "Call sites of this function should be guarded by checking for C++");
 
   if (Tok.is(tok::annot_cxxscope)) {
+    assert(!LastII && "want last identifier but have already annotated scope");
     Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
                                                  Tok.getAnnotationRange(),
                                                  SS);
@@ -188,6 +195,9 @@ bool Parser::ParseOptionalCXXScopeSpecif
     return false;
   }
 
+  if (LastII)
+    *LastII = 0;
+
   bool HasScopeSpecifier = false;
 
   if (Tok.is(tok::coloncolon)) {
@@ -334,6 +344,9 @@ bool Parser::ParseOptionalCXXScopeSpecif
         return false;
       }
 
+      if (LastII)
+        *LastII = TemplateId->Name;
+
       // Consume the template-id token.
       ConsumeToken();
       
@@ -405,6 +418,9 @@ bool Parser::ParseOptionalCXXScopeSpecif
         return false;
       }
 
+      if (LastII)
+        *LastII = ⅈ
+
       // We have an identifier followed by a '::'. Lookup this name
       // as the name in a nested-name-specifier.
       SourceLocation IdLoc = ConsumeToken();

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=177957&r1=177956&r2=177957&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Mon Mar 25 20:15:19 2013
@@ -38,6 +38,43 @@
 using namespace clang;
 using namespace sema;
 
+/// \brief Handle the result of the special case name lookup for inheriting
+/// constructor declarations. 'NS::X::X' and 'NS::X<...>::X' are treated as
+/// constructor names in member using declarations, even if 'X' is not the
+/// name of the corresponding type.
+ParsedType Sema::getInheritingConstructorName(CXXScopeSpec &SS,
+                                              SourceLocation NameLoc,
+                                              IdentifierInfo &Name) {
+  NestedNameSpecifier *NNS = SS.getScopeRep();
+
+  // Convert the nested-name-specifier into a type.
+  QualType Type;
+  switch (NNS->getKind()) {
+  case NestedNameSpecifier::TypeSpec:
+  case NestedNameSpecifier::TypeSpecWithTemplate:
+    Type = QualType(NNS->getAsType(), 0);
+    break;
+
+  case NestedNameSpecifier::Identifier:
+    // Strip off the last layer of the nested-name-specifier and build a
+    // typename type for it.
+    assert(NNS->getAsIdentifier() == &Name && "not a constructor name");
+    Type = Context.getDependentNameType(ETK_None, NNS->getPrefix(),
+                                        NNS->getAsIdentifier());
+    break;
+
+  case NestedNameSpecifier::Global:
+  case NestedNameSpecifier::Namespace:
+  case NestedNameSpecifier::NamespaceAlias:
+    llvm_unreachable("Nested name specifier is not a type for inheriting ctor");
+  }
+
+  // This reference to the type is located entirely at the location of the
+  // final identifier in the qualified-id.
+  return CreateParsedType(Type,
+                          Context.getTrivialTypeSourceInfo(Type, NameLoc));
+}
+
 ParsedType Sema::getDestructorName(SourceLocation TildeLoc,
                                    IdentifierInfo &II,
                                    SourceLocation NameLoc,

Modified: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp?rev=177957&r1=177956&r2=177957&view=diff
==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp (original)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp Mon Mar 25 20:15:19 2013
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
 struct X0 {
   X0 f1();
   X0 f2();
@@ -25,3 +26,94 @@ struct X0::X0 X0::f2() { return X0(); }
 template<typename T> X1<T>::X1<T> X1<T>::f2() { } // expected-error{{qualified reference to 'X1' is a constructor name rather than a template name wherever a constructor can be declared}}
 template<typename T> X1<T>::X1<T> (X1<T>::f2)(int) { } // expected-error{{qualified reference to 'X1' is a constructor name rather than a template name wherever a constructor can be declared}}
 template<typename T> struct X1<T>::X1<T> (X1<T>::f2)(float) { }
+
+// We have a special case for lookup within using-declarations that are
+// member-declarations: foo::bar::baz::baz always names baz's constructor
+// in such a context, even if looking up 'baz' within foo::bar::baz would
+// not find the injected-class-name. Likewise foo::bar::baz<T>::baz also
+// names the constructor.
+namespace InhCtor {
+  struct A {
+    A(int);
+  protected:
+    int T();
+  };
+  typedef A T;
+  struct B : A {
+    // This is a using-declaration for 'int A::T()' in C++98, but is an
+    // inheriting constructor declaration in C++11.
+    using InhCtor::T::T;
+  };
+#if __cplusplus < 201103L
+  B b(123);      // expected-error {{no matching constructor}}
+                 // expected-note at -7 2{{candidate constructor}}
+  int n = b.T(); // ok, accessible
+#else
+  B b(123);      // ok, inheriting constructor
+  int n = b.T(); // expected-error {{'T' is a protected member of 'InhCtor::A'}}
+                 // expected-note at -15 {{declared protected here}}
+
+  template<typename T>
+  struct S : T {
+    struct U : S {
+      using S::S;
+    };
+    using T::T;
+  };
+
+  S<A>::U ua(0);
+  S<B>::U ub(0);
+
+  template<typename T>
+  struct X : T {
+    using T::Z::U::U;
+  };
+  template<typename T>
+  struct X2 : T {
+    using T::Z::template V<int>::V;
+  };
+  struct Y {
+    struct Z {
+      typedef Y U;
+      template<typename T> using V = Y;
+    };
+    Y(int);
+  };
+  X<Y> xy(0);
+
+  namespace Repeat {
+    struct A {
+      struct T {
+        T(int);
+      };
+    };
+    struct Z : A {
+      // FIXME: Core wording says this is invalid, but we and g++ accept.
+      using A::A::A;
+    };
+    template<typename T>
+    struct ZT : T::T {
+      // FIXME: Core wording says this is invalid, but we and g++ accept.
+      using T::T::T;
+    };
+  }
+
+  namespace NS {
+    struct NS {};
+  }
+  struct DerivedFromNS : NS::NS {
+    // No special case unless the NNS names a class.
+    using InhCtor::NS::NS; // expected-error {{using declaration in class refers into 'InhCtor::NS::', which is not a class}}
+
+  };
+
+  typedef int I;
+  struct UsingInt {
+    using I::I; // expected-error {{expected a class or namespace}}
+  };
+  template<typename T> struct UsingIntTemplate {
+    using T::T; // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
+  };
+  UsingIntTemplate<int> uit; // expected-note {{here}}
+#endif
+}





More information about the cfe-commits mailing list