[cfe-commits] r93322 - in /cfe/trunk: include/clang/Basic/DiagnosticParseKinds.td include/clang/Parse/DeclSpec.h include/clang/Parse/Parser.h lib/Parse/DeclSpec.cpp lib/Parse/ParseDecl.cpp lib/Parse/ParseExprCXX.cpp lib/Parse/ParseTemplate.cpp lib/Parse/Parser.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaType.cpp test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp test/CXX/special/class.ctor/p1.cpp test/SemaTemplate/injected-class-name.cpp test/SemaTemplate/instantiate-member-class.cpp

Douglas Gregor dgregor at apple.com
Wed Jan 13 09:31:36 PST 2010


Author: dgregor
Date: Wed Jan 13 11:31:36 2010
New Revision: 93322

URL: http://llvm.org/viewvc/llvm-project?rev=93322&view=rev
Log:
Reimplement constructor declarator parsing to cope with template-ids
that name constructors, the endless joys of out-of-line constructor
definitions, and various other corner cases that the previous hack
never imagined. Fixes PR5688 and tightens up semantic analysis for
constructor names.

Additionally, fixed a problem where we wouldn't properly enter the
declarator scope of a parenthesized declarator. We were entering the
scope, then leaving it when we saw the ")"; now, we re-enter the
declarator scope before parsing the parameter list.

Note that we are forced to perform some tentative parsing within a
class (call it C) to tell the difference between

  C(int); // constructor

and

  C (f)(int); // member function

which is rather unfortunate. And, although it isn't necessary for
correctness, we use the same tentative-parsing mechanism for
out-of-line constructors to improve diagnostics in icky cases like:

  C::C C::f(int); // error: C::C refers to the constructor name, but
                  // we complain nicely and recover by treating it as
                  // a type.


Added:
    cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp   (with props)
    cfe/trunk/test/CXX/special/class.ctor/p1.cpp   (with props)
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/include/clang/Parse/DeclSpec.h
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Parse/DeclSpec.cpp
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/lib/Parse/ParseTemplate.cpp
    cfe/trunk/lib/Parse/Parser.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaType.cpp
    cfe/trunk/test/SemaTemplate/injected-class-name.cpp
    cfe/trunk/test/SemaTemplate/instantiate-member-class.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Wed Jan 13 11:31:36 2010
@@ -291,6 +291,16 @@
     "definition is meant to be an explicit specialization, add '<>' after the "
     "'template' keyword">;
 
+// Constructor template diagnostics.
+def err_out_of_line_constructor_template_id : Error<
+  "out-of-line constructor for %0 cannot have template arguments">;
+def err_out_of_line_template_id_names_constructor : Error<
+  "qualified reference to %0 is a constructor name rather than a "
+  "template name wherever a constructor can be declared">;
+def err_out_of_line_type_names_constructor : Error<
+  "qualified reference to %0 is a constructor name rather than a "
+  "type wherever a constructor can be declared">;
+
 def err_expected_qualified_after_typename : Error<
   "expected a qualified name after 'typename'">;
 def err_typename_refers_to_non_type_template : Error<

Modified: cfe/trunk/include/clang/Parse/DeclSpec.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/DeclSpec.h?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/DeclSpec.h (original)
+++ cfe/trunk/include/clang/Parse/DeclSpec.h Wed Jan 13 11:31:36 2010
@@ -493,6 +493,8 @@
     IK_LiteralOperatorId,
     /// \brief A constructor name.
     IK_ConstructorName,
+    /// \brief A constructor named via a template-id.
+    IK_ConstructorTemplateId,
     /// \brief A destructor name.
     IK_DestructorName,
     /// \brief A template-id, e.g., f<int>.
@@ -534,8 +536,9 @@
     /// class-name.
     ActionBase::TypeTy *DestructorName;
     
-    /// \brief When Kind == IK_TemplateId, the template-id annotation that
-    /// contains the template name and template arguments.
+    /// \brief When Kind == IK_TemplateId or IK_ConstructorTemplateId,
+    /// the template-id annotation that contains the template name and
+    /// template arguments.
     TemplateIdAnnotation *TemplateId;
   };
   
@@ -648,6 +651,14 @@
     ConstructorName = ClassType;
   }
 
+  /// \brief Specify that this unqualified-id was parsed as a
+  /// template-id that names a constructor.
+  ///
+  /// \param TemplateId the template-id annotation that describes the parsed
+  /// template-id. This UnqualifiedId instance will take ownership of the
+  /// \p TemplateId and will free it on destruction.
+  void setConstructorTemplateId(TemplateIdAnnotation *TemplateId);
+
   /// \brief Specify that this unqualified-id was parsed as a destructor name.
   ///
   /// \param TildeLoc the location of the '~' that introduces the destructor

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Wed Jan 13 11:31:36 2010
@@ -1037,7 +1037,8 @@
   /// would be best implemented in the parser.
   enum DeclSpecContext {
     DSC_normal, // normal context
-    DSC_class   // class context, enables 'friend'
+    DSC_class,  // class context, enables 'friend'
+    DSC_top_level // top-level/namespace declaration context
   };
 
   DeclGroupPtrTy ParseDeclaration(unsigned Context, SourceLocation &DeclEnd,
@@ -1056,6 +1057,7 @@
   bool ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
                         const ParsedTemplateInfo &TemplateInfo,
                         AccessSpecifier AS);
+  DeclSpecContext getDeclSpecContextFromDeclaratorContext(unsigned Context);
   void ParseDeclarationSpecifiers(DeclSpec &DS,
                 const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
                                   AccessSpecifier AS = AS_none,
@@ -1109,6 +1111,11 @@
     return isDeclarationSpecifier();
   }
 
+  /// \brief Starting with a scope specifier, identifier, or
+  /// template-id that refers to the current class, determine whether
+  /// this is a constructor declarator.
+  bool isConstructorDeclarator();
+
   /// \brief Specifies the context in which type-id/expression
   /// disambiguation will occur.
   enum TentativeCXXTypeIdContext {

Modified: cfe/trunk/lib/Parse/DeclSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/DeclSpec.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/DeclSpec.cpp (original)
+++ cfe/trunk/lib/Parse/DeclSpec.cpp Wed Jan 13 11:31:36 2010
@@ -36,6 +36,14 @@
   EndLocation = TemplateId->RAngleLoc;
 }
 
+void UnqualifiedId::setConstructorTemplateId(TemplateIdAnnotation *TemplateId) {
+  assert(TemplateId && "NULL template-id annotation?");
+  Kind = IK_ConstructorTemplateId;
+  this->TemplateId = TemplateId;
+  StartLocation = TemplateId->TemplateNameLoc;
+  EndLocation = TemplateId->RAngleLoc;
+}
+
 /// DeclaratorChunk::getFunction - Return a DeclaratorChunk for a function.
 /// "TheDeclarator" is the declarator that this will be added to.
 DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Wed Jan 13 11:31:36 2010
@@ -356,7 +356,8 @@
   ParsingDeclSpec DS(*this);
   if (Attr)
     DS.AddAttributes(Attr);
-  ParseDeclarationSpecifiers(DS);
+  ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none,
+                            getDeclSpecContextFromDeclaratorContext(Context));
 
   // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };"
   // declaration-specifiers init-declarator-list[opt] ';'
@@ -786,6 +787,20 @@
   return false;
 }
 
+/// \brief Determine the declaration specifier context from the declarator
+/// context.
+///
+/// \param Context the declarator context, which is one of the
+/// Declarator::TheContext enumerator values.
+Parser::DeclSpecContext 
+Parser::getDeclSpecContextFromDeclaratorContext(unsigned Context) {
+  if (Context == Declarator::MemberContext)
+    return DSC_class;
+  if (Context == Declarator::FileContext)
+    return DSC_top_level;
+  return DSC_normal;
+}
+
 /// ParseDeclarationSpecifiers
 ///       declaration-specifiers: [C99 6.7]
 ///         storage-class-specifier declaration-specifiers[opt]
@@ -861,6 +876,47 @@
           static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue())
             ->Kind == TNK_Type_template) {
         // We have a qualified template-id, e.g., N::A<int>
+
+        // C++ [class.qual]p2:
+        //   In a lookup in which the constructor is an acceptable lookup
+        //   result and the nested-name-specifier nominates a class C:
+        //
+        //     - if the name specified after the
+        //       nested-name-specifier, when looked up in C, is the
+        //       injected-class-name of C (Clause 9), or
+        //
+        //     - 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 instead considered to name the constructor of
+        //   class C.
+        // 
+        // Thus, if the template-name is actually the constructor
+        // name, then the code is ill-formed; this interpretation is
+        // reinforced by the NAD status of core issue 635. 
+        TemplateIdAnnotation *TemplateId
+          = static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue());
+        if (DSContext == DSC_top_level && TemplateId->Name &&
+            Actions.isCurrentClassName(*TemplateId->Name, CurScope, &SS)) {
+          if (isConstructorDeclarator()) {
+            // The user meant this to be an out-of-line constructor
+            // definition, but template arguments are not allowed
+            // there.  Just allow this as a constructor; we'll
+            // complain about it later.
+            goto DoneWithDeclSpec;
+          }
+
+          // The user meant this to name a type, but it actually names
+          // a constructor with some extraneous template
+          // arguments. Complain, then parse it as a type as the user
+          // intended.
+          Diag(TemplateId->TemplateNameLoc,
+               diag::err_out_of_line_template_id_names_constructor)
+            << TemplateId->Name;
+        }
+
         DS.getTypeSpecScope() = SS;
         ConsumeToken(); // The C++ scope.
         assert(Tok.is(tok::annot_template_id) &&
@@ -885,13 +941,23 @@
       if (Next.isNot(tok::identifier))
         goto DoneWithDeclSpec;
 
-      // If the next token is the name of the class type that the C++ scope
-      // denotes, followed by a '(', then this is a constructor declaration.
-      // We're done with the decl-specifiers.
-      if (Actions.isCurrentClassName(*Next.getIdentifierInfo(),
-                                     CurScope, &SS) &&
-          GetLookAheadToken(2).is(tok::l_paren))
-        goto DoneWithDeclSpec;
+      // If we're in a context where the identifier could be a class name,
+      // check whether this is a constructor declaration.
+      if (DSContext == DSC_top_level &&
+          Actions.isCurrentClassName(*Next.getIdentifierInfo(), CurScope, 
+                                     &SS)) {
+        if (isConstructorDeclarator())
+          goto DoneWithDeclSpec;
+
+        // As noted in C++ [class.qual]p2 (cited above), when the name
+        // of the class is qualified in a context where it could name
+        // a constructor, its a constructor name. However, we've
+        // looked at the declarator, and the user probably meant this
+        // to be a type. Complain that it isn't supposed to be treated
+        // as a type, then proceed to parse it as a type.
+        Diag(Next.getLocation(), diag::err_out_of_line_type_names_constructor)
+          << Next.getIdentifierInfo();
+      }
 
       TypeTy *TypeRep = Actions.getTypeName(*Next.getIdentifierInfo(),
                                             Next.getLocation(), CurScope, &SS);
@@ -972,16 +1038,11 @@
         goto DoneWithDeclSpec;
       }
 
-      // C++: If the identifier is actually the name of the class type
-      // being defined and the next token is a '(', then this is a
-      // constructor declaration. We're done with the decl-specifiers
-      // and will treat this token as an identifier.
-      if (getLang().CPlusPlus &&
-          (CurScope->isClassScope() ||
-           (CurScope->isTemplateParamScope() &&
-            CurScope->getParent()->isClassScope())) &&
+      // If we're in a context where the identifier could be a class name,
+      // check whether this is a constructor declaration.
+      if (getLang().CPlusPlus && DSContext == DSC_class &&
           Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope) &&
-          NextToken().getKind() == tok::l_paren)
+          isConstructorDeclarator())
         goto DoneWithDeclSpec;
 
       isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
@@ -1024,6 +1085,14 @@
         goto DoneWithDeclSpec;
       }
 
+      // If we're in a context where the template-id could be a
+      // constructor name or specialization, check whether this is a
+      // constructor declaration.
+      if (getLang().CPlusPlus && DSContext == DSC_class &&
+          Actions.isCurrentClassName(*TemplateId->Name, CurScope) &&
+          isConstructorDeclarator())
+        goto DoneWithDeclSpec;
+
       // Turn the template-id annotation token into a type annotation
       // token, then try again to parse it as a type-specifier.
       AnnotateTemplateIdTokenAsType();
@@ -2089,6 +2158,48 @@
   }
 }
 
+bool Parser::isConstructorDeclarator() {
+  TentativeParsingAction TPA(*this);
+
+  // Parse the C++ scope specifier.
+  CXXScopeSpec SS;
+  ParseOptionalCXXScopeSpecifier(SS, 0, true);
+
+  // Parse the constructor name.
+  if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id)) {
+    // We already know that we have a constructor name; just consume
+    // the token.
+    ConsumeToken();
+  } else {
+    TPA.Revert();
+    return false;
+  }
+
+  // Current class name must be followed by a left parentheses.
+  if (Tok.isNot(tok::l_paren)) {
+    TPA.Revert();
+    return false;
+  }
+  ConsumeParen();
+
+  // A right parentheses or ellipsis signals that we have a constructor.
+  if (Tok.is(tok::r_paren) || Tok.is(tok::ellipsis)) {
+    TPA.Revert();
+    return true;
+  }
+
+  // If we need to, enter the specified scope.
+  DeclaratorScopeObj DeclScopeObj(*this, SS);
+  if (SS.isSet() && Actions.ShouldEnterDeclaratorScope(CurScope, SS))
+    DeclScopeObj.EnterDeclaratorScope();
+
+  // Check whether the next token(s) are part of a declaration
+  // specifier, in which case we have the start of a parameter and,
+  // therefore, we know that this is a constructor.
+  bool IsConstructor = isDeclarationSpecifier();
+  TPA.Revert();
+  return IsConstructor;
+}
 
 /// ParseTypeQualifierListOpt
 ///       type-qualifier-list: [C99 6.7.5]
@@ -2373,10 +2484,16 @@
         Tok.is(tok::annot_template_id) || Tok.is(tok::tilde)) {
       // We found something that indicates the start of an unqualified-id.
       // Parse that unqualified-id.
+      bool AllowConstructorName
+        = ((D.getCXXScopeSpec().isSet() && 
+            D.getContext() == Declarator::FileContext) ||
+           (!D.getCXXScopeSpec().isSet() &&
+            D.getContext() == Declarator::MemberContext)) &&
+        !D.getDeclSpec().hasTypeSpecifier();
       if (ParseUnqualifiedId(D.getCXXScopeSpec(), 
                              /*EnteringContext=*/true, 
                              /*AllowDestructorName=*/true, 
-                   /*AllowConstructorName=*/!D.getDeclSpec().hasTypeSpecifier(),
+                             AllowConstructorName,
                              /*ObjectType=*/0,
                              D.getName())) {
         D.SetIdentifier(0, Tok.getLocation());
@@ -2403,6 +2520,16 @@
     // direct-declarator: '(' attributes declarator ')'
     // Example: 'char (*X)'   or 'int (*XX)(void)'
     ParseParenDeclarator(D);
+
+    // If the declarator was parenthesized, we entered the declarator
+    // scope when parsing the parenthesized declarator, then exited
+    // the scope already. Re-enter the scope, if we need to.
+    if (D.getCXXScopeSpec().isSet()) {
+      if (Actions.ShouldEnterDeclaratorScope(CurScope, D.getCXXScopeSpec()))
+        // Change the declaration context for name lookup, until this function
+        // is exited (and the declarator has been parsed).
+        DeclScopeObj.EnterDeclaratorScope();
+    }
   } else if (D.mayOmitIdentifier()) {
     // This could be something simple like "int" (in which case the declarator
     // portion is empty), if an abstract-declarator is allowed.

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Wed Jan 13 11:31:36 2010
@@ -1182,12 +1182,41 @@
   // unqualified-id:
   //   template-id (already parsed and annotated)
   if (Tok.is(tok::annot_template_id)) {
-    // FIXME: Could this be a constructor name???
-    
+    TemplateIdAnnotation *TemplateId
+      = static_cast<TemplateIdAnnotation*>(Tok.getAnnotationValue());
+
+    // If the template-name names the current class, then this is a constructor 
+    if (AllowConstructorName && TemplateId->Name &&
+        Actions.isCurrentClassName(*TemplateId->Name, CurScope, &SS)) {
+      if (SS.isSet()) {
+        // C++ [class.qual]p2 specifies that a qualified template-name
+        // is taken as the constructor name where a constructor can be
+        // declared. Thus, the template arguments are extraneous, so
+        // complain about them and remove them entirely.
+        Diag(TemplateId->TemplateNameLoc, 
+             diag::err_out_of_line_constructor_template_id)
+          << TemplateId->Name
+          << CodeModificationHint::CreateRemoval(
+                    SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc));
+        Result.setConstructorName(Actions.getTypeName(*TemplateId->Name,
+                                                  TemplateId->TemplateNameLoc, 
+                                                      CurScope,
+                                                      &SS, false),
+                                  TemplateId->TemplateNameLoc, 
+                                  TemplateId->RAngleLoc);
+        TemplateId->Destroy();
+        ConsumeToken();
+        return false;
+      }
+
+      Result.setConstructorTemplateId(TemplateId);
+      ConsumeToken();
+      return false;
+    }
+
     // We have already parsed a template-id; consume the annotation token as
     // our unqualified-id.
-    Result.setTemplateId(
-                  static_cast<TemplateIdAnnotation*>(Tok.getAnnotationValue()));
+    Result.setTemplateId(TemplateId);
     ConsumeToken();
     return false;
   }

Modified: cfe/trunk/lib/Parse/ParseTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTemplate.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseTemplate.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTemplate.cpp Wed Jan 13 11:31:36 2010
@@ -196,7 +196,8 @@
   if (getLang().CPlusPlus0x && isCXX0XAttributeSpecifier())
     DS.AddAttributes(ParseCXX0XAttributes().AttrList);
 
-  ParseDeclarationSpecifiers(DS, TemplateInfo, AS);
+  ParseDeclarationSpecifiers(DS, TemplateInfo, AS,
+                             getDeclSpecContextFromDeclaratorContext(Context));
 
   if (Tok.is(tok::semi)) {
     DeclEnd = ConsumeToken();

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Wed Jan 13 11:31:36 2010
@@ -541,7 +541,7 @@
   if (Attr)
     DS.AddAttributes(Attr);
 
-  ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS);
+  ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_top_level);
 
   // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };"
   // declaration-specifiers init-declarator-list[opt] ';'

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Jan 13 11:31:36 2010
@@ -1972,6 +1972,30 @@
                                                   Context.getCanonicalType(Ty));
     }
       
+    case UnqualifiedId::IK_ConstructorTemplateId: {
+      // In well-formed code, we can only have a constructor
+      // template-id that refers to the current context, so go there
+      // to find the actual type being constructed.
+      CXXRecordDecl *CurClass = dyn_cast<CXXRecordDecl>(CurContext);
+      if (!CurClass || CurClass->getIdentifier() != Name.TemplateId->Name)
+        return DeclarationName();
+
+      // Determine the type of the class being constructed.
+      QualType CurClassType;
+      if (ClassTemplateDecl *ClassTemplate
+            = CurClass->getDescribedClassTemplate())
+        CurClassType = ClassTemplate->getInjectedClassNameType(Context);
+      else
+        CurClassType = Context.getTypeDeclType(CurClass);
+
+      // FIXME: Check two things: that the template-id names the same type as
+      // CurClassType, and that the template-id does not occur when the name
+      // was qualified.
+
+      return Context.DeclarationNames.getCXXConstructorName(
+                                       Context.getCanonicalType(CurClassType));
+    }
+
     case UnqualifiedId::IK_DestructorName: {
       QualType Ty = GetTypeFromParser(Name.DestructorName);
       if (Ty.isNull())

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Jan 13 11:31:36 2010
@@ -3002,6 +3002,7 @@
     break;
       
   case UnqualifiedId::IK_ConstructorName:
+  case UnqualifiedId::IK_ConstructorTemplateId:
     // C++0x inherited constructors.
     if (getLangOptions().CPlusPlus0x) break;
 

Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Wed Jan 13 11:31:36 2010
@@ -897,6 +897,7 @@
     break;
 
   case UnqualifiedId::IK_ConstructorName:
+  case UnqualifiedId::IK_ConstructorTemplateId:
   case UnqualifiedId::IK_DestructorName:
     // Constructors and destructors don't have return types. Use
     // "void" instead. 

Added: 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=93322&view=auto

==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp (added)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp Wed Jan 13 11:31:36 2010
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+struct X0 {
+  X0 f1();
+  X0 f2();
+};
+
+template<typename T>
+struct X1 {
+  X1<T>(int);
+  (X1<T>)(float);
+  X1 f2();
+  X1 f2(int);
+  X1 f2(float);
+};
+
+// Error recovery: out-of-line constructors whose names have template arguments.
+template<typename T> X1<T>::X1<T>(int) { } // expected-error{{out-of-line constructor for 'X1' cannot have template arguments}}
+template<typename T> (X1<T>::X1<T>)(float) { } // expected-error{{out-of-line constructor for 'X1' cannot have template arguments}}
+
+// Error recovery: out-of-line constructor names intended to be types
+X0::X0 X0::f1() { return X0(); } // expected-error{{qualified reference to 'X0' is a constructor name rather than a type wherever a constructor can be declared}}
+
+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) { }

Propchange: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp

------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp

------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp

------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cfe/trunk/test/CXX/special/class.ctor/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/special/class.ctor/p1.cpp?rev=93322&view=auto

==============================================================================
--- cfe/trunk/test/CXX/special/class.ctor/p1.cpp (added)
+++ cfe/trunk/test/CXX/special/class.ctor/p1.cpp Wed Jan 13 11:31:36 2010
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+struct X0 {
+  struct type { };
+
+  X0();
+  X0(int);
+  (X0)(float);
+  X0 (f0)(int);
+  X0 (f0)(type);
+  
+  X0 f1();
+  X0 f1(double);
+};
+
+X0::X0() { }
+(X0::X0)(int) { }
+
+X0 (X0::f0)(int) { return X0(); }
+
+template<typename T>
+struct X1 {
+  struct type { };
+
+  X1<T>();
+  X1<T>(int);
+  (X1<T>)(float);
+  X1(float, float);
+  (X1)(double);
+  X1<T> (f0)(int);
+  X1<T> (f0)(type);
+  X1 (f1)(int);
+  X1 (f1)(type);
+
+  template<typename U> X1(U);
+  X1 f2();
+  X1 f2(int);
+};
+
+template<typename T> X1<T>::X1() { }
+template<typename T> (X1<T>::X1)(double) { }
+template<typename T> X1<T> X1<T>::f1(int) { return 0; }
+template<typename T> X1<T> (X1<T>::f1)(type) { return 0; }

Propchange: cfe/trunk/test/CXX/special/class.ctor/p1.cpp

------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CXX/special/class.ctor/p1.cpp

------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CXX/special/class.ctor/p1.cpp

------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: cfe/trunk/test/SemaTemplate/injected-class-name.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/injected-class-name.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/injected-class-name.cpp (original)
+++ cfe/trunk/test/SemaTemplate/injected-class-name.cpp Wed Jan 13 11:31:36 2010
@@ -11,11 +11,7 @@
   typedef X<int***> *ptr;
 };
 
-// FIXME: EDG rejects this in their strict-conformance mode, but I
-// don't see any wording making this ill-formed.  Actually,
-// [temp.local]p2 might make it ill-formed. Are we "in the scope of
-// the class template specialization?"
-X<float>::X<int> xi = x;
+X<float>::X<int> xi = x; // expected-error{{qualified reference to 'X' is a constructor name rather than a template name wherever a constructor can be declared}}
 
 // [temp.local]p1:
 

Modified: cfe/trunk/test/SemaTemplate/instantiate-member-class.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/instantiate-member-class.cpp?rev=93322&r1=93321&r2=93322&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/instantiate-member-class.cpp (original)
+++ cfe/trunk/test/SemaTemplate/instantiate-member-class.cpp Wed Jan 13 11:31:36 2010
@@ -14,8 +14,8 @@
 X<int>::C *c1;
 X<float>::C *c2;
 
-X<int>::X *xi;
-X<float>::X *xf;
+X<int>::X *xi; // expected-error{{qualified reference to 'X' is a constructor name rather than a type wherever a constructor can be declared}}
+X<float>::X *xf; // expected-error{{qualified reference to 'X' is a constructor name rather than a type wherever a constructor can be declared}}
 
 void test_naming() {
   c1 = c2; // expected-error{{incompatible type assigning 'X<float>::C *', expected 'X<int>::C *'}}





More information about the cfe-commits mailing list