Parsing, representation, and preliminary semantic analysis of destructors.

Implicit declaration of destructors (when necessary).

Extended Declarator to store information about parsed constructors
and destructors; this will be extended to deal with declarators that
name overloaded operators (e.g., "operator +") and user-defined
conversion operators (e.g., "operator int").



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58767 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index d14c5ff..68d2eee 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -836,7 +836,13 @@
 
   virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl);
   
-  virtual DeclTy *ActOnConstructorDeclarator(CXXConstructorDecl *ConDecl);
+  
+  bool CheckConstructorDeclarator(Declarator &D, QualType &R,
+                                  FunctionDecl::StorageClass& SC);
+  bool CheckDestructorDeclarator(Declarator &D, QualType &R,
+                                 FunctionDecl::StorageClass& SC);
+  DeclTy *ActOnConstructorDeclarator(CXXConstructorDecl *Constructor);
+  DeclTy *ActOnDestructorDeclarator(CXXDestructorDecl *Destructor);
 
   //===--------------------------------------------------------------------===//
   // C++ Derived Classes
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c8c253a..38a5c37 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -800,79 +800,16 @@
     }
 
     bool isInline = D.getDeclSpec().isInlineSpecified();
-    bool isVirtual = D.getDeclSpec().isVirtualSpecified();
+    // bool isVirtual = D.getDeclSpec().isVirtualSpecified();
     bool isExplicit = D.getDeclSpec().isExplicitSpecified();
 
     FunctionDecl *NewFD;
-    if (isCurrentClassName(*II, S)) {
+    if (D.getKind() == Declarator::DK_Constructor) {
       // 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);
+      bool isInvalidDecl = CheckConstructorDeclarator(D, R, SC);
 
       // Create the new declaration
       NewFD = CXXConstructorDecl::Create(Context, 
@@ -881,6 +818,23 @@
                                          isExplicit, isInline,
                                          /*isImplicitlyDeclared=*/false);
 
+      if (isInvalidDecl)
+        NewFD->setInvalidDecl();
+    } else if (D.getKind() == Declarator::DK_Destructor) {
+      // This is a C++ destructor declaration.
+      assert(D.getContext() == Declarator::MemberContext &&
+             "Destructor can only be declared in a member context");
+
+      bool isInvalidDecl = CheckDestructorDeclarator(D, R, SC);
+
+      NewFD = CXXDestructorDecl::Create(Context,
+                                        cast<CXXRecordDecl>(CurContext),
+                                        D.getIdentifierLoc(), II, R, 
+                                        isInline,
+                                        /*isImplicitlyDeclared=*/false);
+
+      if (isInvalidDecl)
+        NewFD->setInvalidDecl();
     } else if (D.getContext() == Declarator::MemberContext) {
       // This is a C++ method declaration.
       NewFD = CXXMethodDecl::Create(Context, cast<CXXRecordDecl>(CurContext),
@@ -969,12 +923,14 @@
       }
     }
 
-    // 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);
+    // C++ constructors and destructors are handled by separate
+    // routines, 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 *Constructor = dyn_cast<CXXConstructorDecl>(NewFD))
+      return ActOnConstructorDeclarator(Constructor);
+    else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(NewFD))
+      return ActOnDestructorDeclarator(Destructor);
 
     // 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.
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 53051ff..ea3c175 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -383,6 +383,7 @@
     //   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.
+    // FIXME: this should probably have its own kind of type node.
     TypedefDecl *InjectedClassName 
       = TypedefDecl::Create(Context, Dcl, LBrace, Dcl->getIdentifier(),
                             Context.getTypeDeclType(Dcl), /*PrevDecl=*/0);
@@ -768,7 +769,25 @@
     ClassDecl->addConstructor(Context, CopyConstructor);
   }
 
-  // FIXME: Implicit destructor
+  if (!ClassDecl->getDestructor()) {
+    // C++ [class.dtor]p2:
+    //   If a class has no user-declared destructor, a destructor is
+    //   declared implicitly. An implicitly-declared destructor is an
+    //   inline public member of its class.
+    std::string DestructorName = "~";
+    DestructorName += ClassDecl->getName();
+    CXXDestructorDecl *Destructor 
+      = CXXDestructorDecl::Create(Context, ClassDecl,
+                                  ClassDecl->getLocation(),
+                                  &PP.getIdentifierTable().get(DestructorName),
+                                  Context.getFunctionType(Context.VoidTy,
+                                                          0, 0, false, 0),
+                                  /*isInline=*/true,
+                                  /*isImplicitlyDeclared=*/true);
+    Destructor->setAccess(AS_public);
+    ClassDecl->setDestructor(Destructor);
+  }
+
   // FIXME: Implicit copy assignment operator
 }
 
@@ -783,6 +802,191 @@
   Consumer.HandleTagDeclDefinition(Rec);
 }
 
+/// CheckConstructorDeclarator - Called by ActOnDeclarator to check
+/// the well-formednes of the constructor declarator @p D with type @p
+/// R. If there are any errors in the declarator, this routine will
+/// emit diagnostics and return true. Otherwise, it will return
+/// false. Either way, the type @p R will be updated to reflect a
+/// well-formed type for the constructor.
+bool Sema::CheckConstructorDeclarator(Declarator &D, QualType &R,
+                                      FunctionDecl::StorageClass& SC) {
+  bool isVirtual = D.getDeclSpec().isVirtualSpecified();
+  bool isInvalid = false;
+
+  // 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()));
+    isInvalid = true;
+  }
+  if (SC == FunctionDecl::Static) {
+    Diag(D.getIdentifierLoc(),
+         diag::err_constructor_cannot_be,
+         "static",
+         SourceRange(D.getDeclSpec().getStorageClassSpecLoc()),
+         SourceRange(D.getIdentifierLoc()));
+    isInvalid = true;
+    SC = FunctionDecl::None;
+  }
+  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);
+
+  return isInvalid;
+}
+
+/// CheckDestructorDeclarator - Called by ActOnDeclarator to check
+/// the well-formednes of the destructor declarator @p D with type @p
+/// R. If there are any errors in the declarator, this routine will
+/// emit diagnostics and return true. Otherwise, it will return
+/// false. Either way, the type @p R will be updated to reflect a
+/// well-formed type for the destructor.
+bool Sema::CheckDestructorDeclarator(Declarator &D, QualType &R,
+                                     FunctionDecl::StorageClass& SC) {
+  bool isInvalid = false;
+
+  // C++ [class.dtor]p1:
+  //   [...] A typedef-name that names a class is a class-name
+  //   (7.1.3); however, a typedef-name that names a class shall not
+  //   be used as the identifier in the declarator for a destructor
+  //   declaration.
+  TypeDecl *DeclaratorTypeD = (TypeDecl *)D.getDeclaratorIdType();
+  if (const TypedefDecl *TypedefD = dyn_cast<TypedefDecl>(DeclaratorTypeD)) {
+    if (TypedefD->getIdentifier() != 
+          cast<CXXRecordDecl>(CurContext)->getIdentifier()) {
+      // FIXME: This would be easier if we could just look at whether
+      // we found the injected-class-name.
+      Diag(D.getIdentifierLoc(), 
+           diag::err_destructor_typedef_name,
+           TypedefD->getName());
+      isInvalid = true;
+    }
+  }
+
+  // C++ [class.dtor]p2:
+  //   A destructor is used to destroy objects of its class type. A
+  //   destructor takes no parameters, and no return type can be
+  //   specified for it (not even void). The address of a destructor
+  //   shall not be taken. A destructor shall not be static. A
+  //   destructor can be invoked for a const, volatile or const
+  //   volatile object. A destructor shall not be declared const,
+  //   volatile or const volatile (9.3.2).
+  if (SC == FunctionDecl::Static) {
+    Diag(D.getIdentifierLoc(),
+         diag::err_destructor_cannot_be,
+         "static",
+         SourceRange(D.getDeclSpec().getStorageClassSpecLoc()),
+         SourceRange(D.getIdentifierLoc()));
+    isInvalid = true;
+    SC = FunctionDecl::None;
+  }
+  if (D.getDeclSpec().hasTypeSpecifier()) {
+    // Destructors don't have return types, but the parser will
+    // happily parse something like:
+    //
+    //   class X {
+    //     float ~X();
+    //   };
+    //
+    // The return type will be eliminated later.
+    Diag(D.getIdentifierLoc(),
+         diag::err_destructor_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_destructor,
+           "const",
+           SourceRange(D.getIdentifierLoc()));
+    if (FTI.TypeQuals & QualType::Volatile)
+      Diag(D.getIdentifierLoc(),
+           diag::err_invalid_qualified_destructor,
+           "volatile",
+           SourceRange(D.getIdentifierLoc()));
+    if (FTI.TypeQuals & QualType::Restrict)
+      Diag(D.getIdentifierLoc(),
+           diag::err_invalid_qualified_destructor,
+           "restrict",
+           SourceRange(D.getIdentifierLoc()));
+  }
+
+  // Make sure we don't have any parameters.
+  if (R->getAsFunctionTypeProto()->getNumArgs() > 0) {
+    Diag(D.getIdentifierLoc(), diag::err_destructor_with_params);
+
+    // Delete the parameters.
+    DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun;
+    if (FTI.NumArgs) {
+      delete [] FTI.ArgInfo;
+      FTI.NumArgs = 0;
+      FTI.ArgInfo = 0;
+    }
+  }
+
+  // Make sure the destructor isn't variadic.  
+  if (R->getAsFunctionTypeProto()->isVariadic())
+    Diag(D.getIdentifierLoc(), diag::err_destructor_variadic);
+
+  // Rebuild the function type "R" without any type qualifiers or
+  // parameters (in case any of the errors above fired) and with
+  // "void" as the return type, since destructors 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.
+  R = Context.getFunctionType(Context.VoidTy, 0, 0, false, 0);
+
+  return isInvalid;
+}
+
 /// 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
@@ -837,7 +1041,7 @@
            diag::err_constructor_byvalue_arg,
            SourceRange(ConDecl->getParamDecl(0)->getLocation()));
       ConDecl->setInvalidDecl();
-      return 0;
+      return ConDecl;
     }
   }
       
@@ -847,6 +1051,30 @@
   return (DeclTy *)ConDecl;
 }
 
+/// ActOnDestructorDeclarator - Called by ActOnDeclarator to complete
+/// the declaration of the given C++ @p Destructor. This routine is
+/// responsible for recording the destructor in the C++ class, if
+/// possible.
+Sema::DeclTy *Sema::ActOnDestructorDeclarator(CXXDestructorDecl *Destructor) {
+  assert(Destructor && "Expected to receive a destructor declaration");
+
+  CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(CurContext);
+
+  // Make sure we aren't redeclaring the destructor.
+  if (CXXDestructorDecl *PrevDestructor = ClassDecl->getDestructor()) {
+    Diag(Destructor->getLocation(), diag::err_destructor_redeclared);
+    Diag(PrevDestructor->getLocation(),
+         PrevDestructor->isThisDeclarationADefinition()?
+             diag::err_previous_definition
+           : diag::err_previous_declaration);
+    Destructor->setInvalidDecl();
+    return Destructor;
+  }
+
+  ClassDecl->setDestructor(Destructor);
+  return (DeclTy *)Destructor;
+}
+
 //===----------------------------------------------------------------------===//
 // Namespace Handling
 //===----------------------------------------------------------------------===//