Add support for parsing and representing C++ constructor declarations.

Notes:
  - Constructors are never found by name lookup, so they'll never get
    pushed into any scope. Instead, they are stored as an 
    OverloadedFunctionDecl in CXXRecordDecl for easy overloading.
  - There's a new action isCurrentClassName that determines whether an
    identifier is the name of the innermost class currently being defined;
    we use this to identify the declarator-id grammar rule that refers to 
    a type-name. 
  - MinimalAction does *not* support parsing constructors.
  - We now handle virtual and explicit function specifiers.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58499 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index 686aba2..3c6c91e 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -237,6 +237,7 @@
   case CXXRecord:           nCXXSUC++; break;
   // FIXME: Statistics for C++ decls.
   case CXXMethod:
+  case CXXConstructor:
   case CXXClassVar:
     break;
   }
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index 0d0b746..3ec6824 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -39,6 +39,14 @@
   delete [] Bases;
 }
 
+void CXXRecordDecl::Destroy(ASTContext &C) {
+  for (OverloadedFunctionDecl::function_iterator func 
+         = Constructors.function_begin();
+       func != Constructors.function_end(); ++func)
+    (*func)->Destroy(C);
+  RecordDecl::Destroy(C);
+}
+
 void 
 CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, 
                         unsigned NumBases) {
@@ -74,6 +82,17 @@
   return C.getPointerType(ClassTy).withConst();
 }
 
+CXXConstructorDecl *
+CXXConstructorDecl::Create(ASTContext &C, CXXRecordDecl *RD,
+                           SourceLocation L, IdentifierInfo *Id,
+                           QualType T, bool isExplicit,
+                           bool isInline, bool isImplicitlyDeclared) {
+  void *Mem = C.getAllocator().Allocate<CXXConstructorDecl>();
+  return new (Mem) CXXConstructorDecl(RD, L, Id, T, isExplicit, isInline,
+                                      isImplicitlyDeclared);
+}
+
+
 CXXClassVarDecl *CXXClassVarDecl::Create(ASTContext &C, CXXRecordDecl *RD,
                                    SourceLocation L, IdentifierInfo *Id,
                                    QualType T, ScopedDecl *PrevDecl) {
diff --git a/lib/Parse/DeclSpec.cpp b/lib/Parse/DeclSpec.cpp
index 1d42fed..d61d6c5 100644
--- a/lib/Parse/DeclSpec.cpp
+++ b/lib/Parse/DeclSpec.cpp
@@ -30,7 +30,7 @@
   if (hasTypeSpecifier())
     Res |= PQ_TypeSpecifier;
   
-  if (FS_inline_specified)
+  if (FS_inline_specified || FS_virtual_specified || FS_explicit_specified)
     Res |= PQ_FunctionSpecifier;
   return Res;
 }
@@ -206,6 +206,20 @@
   return false;
 }
 
+bool DeclSpec::SetFunctionSpecVirtual(SourceLocation Loc, const char *&PrevSpec){
+  // 'virtual virtual' is ok.
+  FS_virtual_specified = true;
+  FS_virtualLoc = Loc;
+  return false;
+}
+
+bool DeclSpec::SetFunctionSpecExplicit(SourceLocation Loc, const char *&PrevSpec){
+  // 'explicit explicit' is ok.
+  FS_explicit_specified = true;
+  FS_explicitLoc = Loc;
+  return false;
+}
+
 
 /// Finish - This does final analysis of the declspec, rejecting things like
 /// "_Imaginary" (lacking an FP type).  This returns a diagnostic to issue or
diff --git a/lib/Parse/MinimalAction.cpp b/lib/Parse/MinimalAction.cpp
index c98830c..2fec359 100644
--- a/lib/Parse/MinimalAction.cpp
+++ b/lib/Parse/MinimalAction.cpp
@@ -63,6 +63,12 @@
   return 0;
 }
 
+/// isCurrentClassName - Always returns false, because MinimalAction
+/// does not support C++ classes with constructors.
+bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *) {
+  return false;
+}
+
 /// ActOnDeclarator - If this is a typedef declarator, we modify the
 /// IdentifierInfo::FETokenInfo field to keep track of this fact, until S is
 /// popped.
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 243a15f..7a84171 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -383,7 +383,12 @@
   
   // Issue diagnostic and remove function specfier if present.
   if (Specs & DeclSpec::PQ_FunctionSpecifier) {
-    Diag(DS.getInlineSpecLoc(), diag::err_typename_invalid_functionspec);
+    if (DS.isInlineSpecified())
+      Diag(DS.getInlineSpecLoc(), diag::err_typename_invalid_functionspec);
+    if (DS.isVirtualSpecified())
+      Diag(DS.getVirtualSpecLoc(), diag::err_typename_invalid_functionspec);
+    if (DS.isExplicitSpecified())
+      Diag(DS.getExplicitSpecLoc(), diag::err_typename_invalid_functionspec);
     DS.ClearFunctionSpecs();
   }
 }
@@ -433,6 +438,8 @@
 /// [C99]   'restrict'
 ///       function-specifier: [C99 6.7.4]
 /// [C99]   'inline'
+/// [C++]   'virtual'
+/// [C++]   'explicit'
 ///
 void Parser::ParseDeclarationSpecifiers(DeclSpec &DS) {
   DS.SetRangeStart(Tok.getLocation());
@@ -462,6 +469,16 @@
       if (TypeRep == 0)
         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->isCXXClassScope() &&
+          Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope) && 
+          NextToken().getKind() == tok::l_paren)
+        goto DoneWithDeclSpec;
+
       isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typedef, Loc, PrevSpec,
                                      TypeRep);
       if (isInvalid)
@@ -607,6 +624,14 @@
     case tok::kw_inline:
       isInvalid = DS.SetFunctionSpecInline(Loc, PrevSpec);
       break;
+
+    case tok::kw_virtual:
+      isInvalid = DS.SetFunctionSpecVirtual(Loc, PrevSpec);
+      break;
+
+    case tok::kw_explicit:
+      isInvalid = DS.SetFunctionSpecExplicit(Loc, PrevSpec);
+      break;
       
     case tok::less:
       // GCC ObjC supports types like "<SomeProtocol>" as a synonym for
@@ -1056,6 +1081,8 @@
 
     // function-specifier
   case tok::kw_inline:
+  case tok::kw_virtual:
+  case tok::kw_explicit:
 
     // GNU typeof support.
   case tok::kw_typeof:
@@ -1217,7 +1244,11 @@
 ///                    parameter-type-list[opt] ')'
 /// [C++]   direct-declarator '(' parameter-declaration-clause ')'
 ///                    cv-qualifier-seq[opt] exception-specification[opt]
-///
+/// [C++]   declarator-id
+//
+//        declarator-id: [C++ 8]
+//          id-expression
+//          '::'[opt] nested-name-specifier[opt] type-name
 void Parser::ParseDirectDeclarator(Declarator &D) {
   // Parse the first direct-declarator seen.
   if (Tok.is(tok::identifier) && D.mayHaveIdentifier()) {
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 1cff55e..2a49e71 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -773,7 +773,8 @@
   //===--------------------------------------------------------------------===//
   // C++ Classes
   //
-
+  virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S);
+  
   virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl,
                                      SourceLocation LBrace);
 
@@ -788,6 +789,8 @@
 
   virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl);
   
+  virtual DeclTy *ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl);
+
   //===--------------------------------------------------------------------===//
   // C++ Derived Classes
   //
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 2306151..16053ce 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -737,8 +737,88 @@
     }
 
     bool isInline = D.getDeclSpec().isInlineSpecified();
+    bool isVirtual = D.getDeclSpec().isVirtualSpecified();
+    bool isExplicit = D.getDeclSpec().isExplicitSpecified();
+
     FunctionDecl *NewFD;
-    if (D.getContext() == Declarator::MemberContext) {
+    if (isCurrentClassName(*II, S)) {
+      // This is a C++ constructor declaration.
+      assert(D.getContext() == Declarator::MemberContext &&
+             "Constructors can only be declared in a member context");
+
+      // C++ [class.ctor]p3:
+      //   A constructor shall not be virtual (10.3) or static (9.4). A
+      //   constructor can be invoked for a const, volatile or const
+      //   volatile object. A constructor shall not be declared const,
+      //   volatile, or const volatile (9.3.2).
+      if (isVirtual) {
+        Diag(D.getIdentifierLoc(),
+             diag::err_constructor_cannot_be,
+             "virtual",
+             SourceRange(D.getDeclSpec().getVirtualSpecLoc()),
+             SourceRange(D.getIdentifierLoc()));
+        isVirtual = false;
+      }
+      if (SC == FunctionDecl::Static) {
+        Diag(D.getIdentifierLoc(),
+             diag::err_constructor_cannot_be,
+             "static",
+             SourceRange(D.getDeclSpec().getStorageClassSpecLoc()),
+             SourceRange(D.getIdentifierLoc()));
+        isVirtual = false;
+      }
+      if (D.getDeclSpec().hasTypeSpecifier()) {
+        // Constructors don't have return types, but the parser will
+        // happily parse something like:
+        //
+        //   class X {
+        //     float X(float);
+        //   };
+        //
+        // The return type will be eliminated later.
+        Diag(D.getIdentifierLoc(),
+             diag::err_constructor_return_type,
+             SourceRange(D.getDeclSpec().getTypeSpecTypeLoc()),
+             SourceRange(D.getIdentifierLoc()));
+      } 
+      if (R->getAsFunctionTypeProto()->getTypeQuals() != 0) {
+        DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun;
+        if (FTI.TypeQuals & QualType::Const)
+          Diag(D.getIdentifierLoc(),
+               diag::err_invalid_qualified_constructor,
+               "const",
+               SourceRange(D.getIdentifierLoc()));
+        if (FTI.TypeQuals & QualType::Volatile)
+          Diag(D.getIdentifierLoc(),
+               diag::err_invalid_qualified_constructor,
+               "volatile",
+               SourceRange(D.getIdentifierLoc()));
+        if (FTI.TypeQuals & QualType::Restrict)
+          Diag(D.getIdentifierLoc(),
+               diag::err_invalid_qualified_constructor,
+               "restrict",
+               SourceRange(D.getIdentifierLoc()));
+      }
+      
+      // Rebuild the function type "R" without any type qualifiers (in
+      // case any of the errors above fired) and with "void" as the
+      // return type, since constructors don't have return types. We
+      // *always* have to do this, because GetTypeForDeclarator will
+      // put in a result type of "int" when none was specified.
+      const FunctionTypeProto *Proto = R->getAsFunctionTypeProto();
+      R = Context.getFunctionType(Context.VoidTy, Proto->arg_type_begin(),
+                                  Proto->getNumArgs(),
+                                  Proto->isVariadic(),
+                                  0);
+
+      // Create the new declaration
+      NewFD = CXXConstructorDecl::Create(Context, 
+                                         cast<CXXRecordDecl>(CurContext),
+                                         D.getIdentifierLoc(), II, R,
+                                         isExplicit, isInline,
+                                         /*isImplicitlyDeclared=*/false);
+
+    } else if (D.getContext() == Declarator::MemberContext) {
       // This is a C++ method declaration.
       NewFD = CXXMethodDecl::Create(Context, cast<CXXRecordDecl>(CurContext),
                                     D.getIdentifierLoc(), II, R,
@@ -826,6 +906,13 @@
       }
     }
 
+    // C++ constructors are handled by a separate routine, since they
+    // don't require any declaration merging (C++ [class.mfct]p2) and
+    // they aren't ever pushed into scope, because they can't be found
+    // by name lookup anyway (C++ [class.ctor]p2).
+    if (CXXConstructorDecl *ConDecl = dyn_cast<CXXConstructorDecl>(NewFD))
+      return ActOnConstructorDeclarator(ConDecl);
+
     // Merge the decl with the existing one if appropriate. Since C functions
     // are in a flat namespace, make sure we consider decls in outer scopes.
     if (PrevDecl &&
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 817204c..d5b6312 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -257,6 +257,17 @@
   }
 }
 
+/// isCurrentClassName - Determine whether the identifier II is the
+/// name of the class type currently being defined. In the case of
+/// nested classes, this will only return true if II is the name of
+/// the innermost class.
+bool Sema::isCurrentClassName(const IdentifierInfo &II, Scope *) {
+  if (CXXRecordDecl *CurDecl = dyn_cast_or_null<CXXRecordDecl>(CurContext))
+    return &II == CurDecl->getIdentifier();
+  else
+    return false;
+}
+
 /// ActOnBaseSpecifier - Parsed a base specifier. A base specifier is
 /// one entry in the base class list of a class specifier, for
 /// example: 
@@ -361,9 +372,21 @@
 /// ActOnStartCXXClassDef - This is called at the start of a class/struct/union
 /// definition, when on C++.
 void Sema::ActOnStartCXXClassDef(Scope *S, DeclTy *D, SourceLocation LBrace) {
-  Decl *Dcl = static_cast<Decl *>(D);
-  PushDeclContext(cast<CXXRecordDecl>(Dcl));
+  CXXRecordDecl *Dcl = cast<CXXRecordDecl>(static_cast<Decl *>(D));
+  PushDeclContext(Dcl);
   FieldCollector->StartClass();
+
+  if (Dcl->getIdentifier()) {
+    // C++ [class]p2: 
+    //   [...] The class-name is also inserted into the scope of the
+    //   class itself; this is known as the injected-class-name. For
+    //   purposes of access checking, the injected-class-name is treated
+    //   as if it were a public member name.
+    TypedefDecl *InjectedClassName 
+      = TypedefDecl::Create(Context, Dcl, LBrace, Dcl->getIdentifier(),
+                            Context.getTypeDeclType(Dcl), /*PrevDecl=*/0);
+    PushOnScopeChains(InjectedClassName, S);
+  }
 }
 
 /// ActOnCXXMemberDeclarator - This is invoked when a C++ class member
@@ -539,6 +562,22 @@
   Consumer.HandleTagDeclDefinition(Rec);
 }
 
+/// ActOnConstructorDeclarator - Called by ActOnDeclarator to complete
+/// the declaration of the given C++ constructor ConDecl that was
+/// built from declarator D. This routine is responsible for checking
+/// that the newly-created constructor declaration is well-formed and
+/// for recording it in the C++ class. Example:
+///
+/// @code 
+/// class X {
+///   X(); // X::X() will be the ConDecl.
+/// };
+/// @endcode
+Sema::DeclTy *Sema::ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl) {
+  assert(ConDecl && "Expected to receive a constructor declaration");
+  return (DeclTy *)ConDecl;
+}
+
 //===----------------------------------------------------------------------===//
 // Namespace Handling
 //===----------------------------------------------------------------------===//