[cfe-commits] r131115 - in /cfe/trunk: include/clang/AST/DeclCXX.h include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclCXX.cpp

Sean Hunt scshunt at csclub.uwaterloo.ca
Mon May 9 17:49:42 PDT 2011


Author: coppro
Date: Mon May  9 19:49:42 2011
New Revision: 131115

URL: http://llvm.org/viewvc/llvm-project?rev=131115&view=rev
Log:
Further implement defaulting constructors.

Focus is on default constructors for the time being. Currently the
exception specification and prototype are processed correctly. Codegen
might work but in all likelihood doesn't.

Note that due to an error, out-of-line defaulting of member functions is
currently impossible. It will continue to that until I muster up the
courage to admit that I secretly pray to epimetheus and that I need to
rework the way default gets from Parse -> Sema.

Modified:
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=131115&r1=131114&r2=131115&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Mon May  9 19:49:42 2011
@@ -682,7 +682,7 @@
   bool needsImplicitDefaultConstructor() const {
     return data().NeedsImplicitDefaultConstructor;
   }
-  
+
   /// hasConstCopyConstructor - Determines whether this class has a
   /// copy constructor that accepts a const-qualified argument.
   bool hasConstCopyConstructor(const ASTContext &Context) const;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=131115&r1=131114&r2=131115&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May  9 19:49:42 2011
@@ -2149,6 +2149,9 @@
 def err_definition_of_implicitly_declared_member : Error<
   "definition of implicitly declared %select{constructor|copy constructor|"
   "copy assignment operator|destructor}1">;
+def err_definition_of_explicitly_defaulted_member : Error<
+  "definition of explicitly defaulted %select{constructor|copy constructor|"
+  "copy assignment operator|destructor}0">;
 def err_redefinition_extern_inline : Error<
   "redefinition of a 'extern inline' function %0 is not supported in "
   "%select{C99 mode|C++}1">;
@@ -3619,6 +3622,12 @@
 def warn_explicit_conversion_functions : Warning<
   "explicit conversion functions are a C++0x extension">, InGroup<CXX0x>;
 
+// C++0x defaulted functions
+def err_defaulted_default_ctor_params : Error<
+  "an explicitly-defaulted default constructor must have no parameters">;
+def err_incorrect_defaulted_exception_spec : Error<
+  "exception specification of explicitly defaulted function is incorrect">;
+
 def warn_array_index_precedes_bounds : Warning<
   "array index of '%0' indexes before the beginning of the array">,
   InGroup<DiagGroup<"array-bounds">>;

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=131115&r1=131114&r2=131115&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon May  9 19:49:42 2011
@@ -2526,6 +2526,62 @@
   /// constructed variable.
   void FinalizeVarWithDestructor(VarDecl *VD, const RecordType *DeclInitType);
 
+  /// \brief Helper class that collects exception specifications for 
+  /// implicitly-declared special member functions.
+  class ImplicitExceptionSpecification {
+    ASTContext &Context;
+    // We order exception specifications thus:
+    // noexcept is the most restrictive, but is only used in C++0x.
+    // throw() comes next.
+    // Then a throw(collected exceptions)
+    // Finally no specification.
+    // throw(...) is used instead if any called function uses it.
+    ExceptionSpecificationType ComputedEST;
+    llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
+    llvm::SmallVector<QualType, 4> Exceptions;
+
+    void ClearExceptions() {
+      ExceptionsSeen.clear();
+      Exceptions.clear();
+    }
+
+  public:
+    explicit ImplicitExceptionSpecification(ASTContext &Context) 
+      : Context(Context), ComputedEST(EST_BasicNoexcept) {
+      if (!Context.getLangOptions().CPlusPlus0x)
+        ComputedEST = EST_DynamicNone;
+    }
+
+    /// \brief Get the computed exception specification type.
+    ExceptionSpecificationType getExceptionSpecType() const {
+      assert(ComputedEST != EST_ComputedNoexcept &&
+             "noexcept(expr) should not be a possible result");
+      return ComputedEST;
+    }
+
+    /// \brief The number of exceptions in the exception specification.
+    unsigned size() const { return Exceptions.size(); }
+
+    /// \brief The set of exceptions in the exception specification.
+    const QualType *data() const { return Exceptions.data(); }
+
+    /// \brief Integrate another called method into the collected data.
+    void CalledDecl(CXXMethodDecl *Method);
+
+    FunctionProtoType::ExtProtoInfo getEPI() const {
+      FunctionProtoType::ExtProtoInfo EPI;
+      EPI.ExceptionSpecType = getExceptionSpecType();
+      EPI.NumExceptions = size();
+      EPI.Exceptions = data();
+      return EPI;
+    }
+  };
+
+  /// \brief Determine what sort of exception specification a defaulted
+  /// constructor of a class will have.
+  ImplicitExceptionSpecification
+  ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl);
+
   /// \brief Declare the implicit default constructor for the given class.
   ///
   /// \param ClassDecl The class declaration into which the implicit 
@@ -3199,6 +3255,9 @@
                                  StorageClass& SC);
   Decl *ActOnConversionDeclarator(CXXConversionDecl *Conversion);
 
+  void CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record);
+  void CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *Ctor);
+
   //===--------------------------------------------------------------------===//
   // C++ Derived Classes
   //

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=131115&r1=131114&r2=131115&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon May  9 19:49:42 2011
@@ -1730,6 +1730,11 @@
             << New << getSpecialMember(OldMethod);
           return true;
         }
+      } else if (OldMethod->isExplicitlyDefaulted()) {
+        Diag(NewMethod->getLocation(),
+             diag::err_definition_of_explicitly_defaulted_member)
+          << getSpecialMember(OldMethod);
+        return true;
       }
     }
 
@@ -4137,16 +4142,6 @@
                                          /*isImplicitlyDeclared=*/false);
 
       NewFD = NewCD;
-
-      if (DefaultLoc.isValid()) {
-        if (NewCD->isDefaultConstructor() ||
-            NewCD->isCopyOrMoveConstructor()) {
-          NewFD->setDefaulted();
-          NewFD->setExplicitlyDefaulted();
-        } else {
-          Diag(DefaultLoc, diag::err_default_special_members);
-        }
-      }
     } else if (Name.getNameKind() == DeclarationName::CXXDestructorName) {
       // This is a C++ destructor declaration.
       if (DC->isRecord()) {
@@ -4159,11 +4154,6 @@
                                           isInline,
                                           /*isImplicitlyDeclared=*/false);
         isVirtualOkay = true;
-
-        if (DefaultLoc.isValid()) {
-          NewFD->setDefaulted();
-          NewFD->setExplicitlyDefaulted();
-        }
       } else {
         Diag(D.getIdentifierLoc(), diag::err_destructor_not_member);
 
@@ -4182,9 +4172,6 @@
         return 0;
       }
 
-      if (DefaultLoc.isValid())
-        Diag(DefaultLoc, diag::err_default_special_members);
-
       CheckConversionDeclarator(D, R, SC);
       NewFD = CXXConversionDecl::Create(Context, cast<CXXRecordDecl>(DC),
                                         D.getSourceRange().getBegin(),
@@ -4232,16 +4219,6 @@
       NewFD = NewMD;
 
       isVirtualOkay = !isStatic;
-
-      if (DefaultLoc.isValid()) {
-        if (NewMD->isCopyAssignmentOperator() /* ||
-            NewMD->isMoveAssignmentOperator() */) {
-          NewFD->setDefaulted();
-          NewFD->setExplicitlyDefaulted();
-        } else {
-          Diag(DefaultLoc, diag::err_default_special_members);
-        }
-      }
     } else {
       if (DefaultLoc.isValid())
         Diag(DefaultLoc, diag::err_default_special_members);
@@ -4715,7 +4692,7 @@
 
     } else if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet() &&
                !isFriend && !isFunctionTemplateSpecialization &&
-               !isExplicitSpecialization) {
+               !isExplicitSpecialization && !DefaultLoc.isValid()) {
       // An out-of-line member function declaration must also be a
       // definition (C++ [dcl.meaning]p1).
       // Note that this is not the case for explicit specializations of
@@ -4800,6 +4777,36 @@
         }
       }
 
+  // Check explicitly defaulted methods
+  // FIXME: This could be made better through CXXSpecialMember if it did
+  // default constructors (which it should rather than any constructor).
+  if (NewFD && DefaultLoc.isValid() && getLangOptions().CPlusPlus) {
+    if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD)) {
+      if (CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(MD)) {
+        if (CD->isDefaultConstructor() || CD->isCopyOrMoveConstructor()) {
+          CD->setDefaulted();
+          CD->setExplicitlyDefaulted();
+          if (CD != CD->getCanonicalDecl() && CD->isDefaultConstructor())
+            CheckExplicitlyDefaultedDefaultConstructor(CD);
+          // FIXME: Do copy/move ctors here.
+        } else {
+          Diag(DefaultLoc, diag::err_default_special_members);
+        }
+      } else if (CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
+        DD->setDefaulted();
+        DD->setExplicitlyDefaulted();
+        // FIXME: Add a checking method
+      } else if (MD->isCopyAssignmentOperator() /* ||
+                 MD->isMoveAssignmentOperator() */) {
+        MD->setDefaulted();
+        MD->setExplicitlyDefaulted();
+        // FIXME: Add a checking method
+      } else {
+        Diag(DefaultLoc, diag::err_default_special_members);
+      }
+    }
+  }
+
   return NewFD;
 }
 

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=131115&r1=131114&r2=131115&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon May  9 19:49:42 2011
@@ -111,6 +111,69 @@
   }
 }
 
+void Sema::ImplicitExceptionSpecification::CalledDecl(CXXMethodDecl *Method) {
+  // If we have an MSAny spec already, don't bother.
+  if (!Method || ComputedEST == EST_MSAny)
+    return;
+
+  const FunctionProtoType *Proto
+    = Method->getType()->getAs<FunctionProtoType>();
+
+  ExceptionSpecificationType EST = Proto->getExceptionSpecType();
+
+  // If this function can throw any exceptions, make a note of that.
+  if (EST == EST_MSAny || EST == EST_None) {
+    ClearExceptions();
+    ComputedEST = EST;
+    return;
+  }
+
+  // If this function has a basic noexcept, it doesn't affect the outcome.
+  if (EST == EST_BasicNoexcept)
+    return;
+
+  // If we have a throw-all spec at this point, ignore the function.
+  if (ComputedEST == EST_None)
+    return;
+
+  // If we're still at noexcept(true) and there's a nothrow() callee,
+  // change to that specification.
+  if (EST == EST_DynamicNone) {
+    if (ComputedEST == EST_BasicNoexcept)
+      ComputedEST = EST_DynamicNone;
+    return;
+  }
+
+  // Check out noexcept specs.
+  if (EST == EST_ComputedNoexcept) {
+    FunctionProtoType::NoexceptResult NR = Proto->getNoexceptSpec(Context);
+    assert(NR != FunctionProtoType::NR_NoNoexcept &&
+           "Must have noexcept result for EST_ComputedNoexcept.");
+    assert(NR != FunctionProtoType::NR_Dependent &&
+           "Should not generate implicit declarations for dependent cases, "
+           "and don't know how to handle them anyway.");
+
+    // noexcept(false) -> no spec on the new function
+    if (NR == FunctionProtoType::NR_Throw) {
+      ClearExceptions();
+      ComputedEST = EST_None;
+    }
+    // noexcept(true) won't change anything either.
+    return;
+  }
+
+  assert(EST == EST_Dynamic && "EST case not considered earlier.");
+  assert(ComputedEST != EST_None &&
+         "Shouldn't collect exceptions when throw-all is guaranteed.");
+  ComputedEST = EST_Dynamic;
+  // Record the exceptions in this function's exception specification.
+  for (FunctionProtoType::exception_iterator E = Proto->exception_begin(),
+                                          EEnd = Proto->exception_end();
+       E != EEnd; ++E)
+    if (ExceptionsSeen.insert(Context.getCanonicalType(*E)))
+      Exceptions.push_back(*E);
+}
+
 bool
 Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg,
                               SourceLocation EqualLoc) {
@@ -2937,6 +3000,64 @@
   //   instantiated (e.g. meta-functions). This doesn't apply to classes that
   //   have inherited constructors.
   DeclareInheritedConstructors(Record);
+
+  CheckExplicitlyDefaultedMethods(Record);
+}
+
+void Sema::CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record) {
+  for (CXXRecordDecl::ctor_iterator CI = Record->ctor_begin(),
+                                    CE = Record->ctor_end();
+       CI != CE; ++CI) {
+    if (!CI->isInvalidDecl() && CI->isExplicitlyDefaulted()) {
+      if (CI->isDefaultConstructor()) {
+        CheckExplicitlyDefaultedDefaultConstructor(*CI);
+      }
+
+      // FIXME: Do copy and move constructors
+    }
+  }
+
+  // FIXME: Do copy and move assignment and destructors
+}
+
+void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) {
+  assert(CD->isExplicitlyDefaulted() && CD->isDefaultConstructor());
+  
+  // Whether this was the first-declared instance of the constructor.
+  // This affects whether we implicitly add an exception spec (and, eventually,
+  // constexpr). It is also ill-formed to explicitly default a constructor such
+  // that it would be deleted. (C++0x [decl.fct.def.default])
+  bool First = CD == CD->getCanonicalDecl();
+
+  if (CD->getNumParams() != 0) {
+    Diag(CD->getLocation(), diag::err_defaulted_default_ctor_params)
+      << CD->getSourceRange();
+    CD->setInvalidDecl();
+    return;
+  }
+
+  ImplicitExceptionSpecification Spec
+    = ComputeDefaultedDefaultCtorExceptionSpec(CD->getParent());
+  FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
+  const FunctionProtoType *CtorType = CD->getType()->getAs<FunctionProtoType>(),
+                          *ExceptionType = Context.getFunctionType(
+                         Context.VoidTy, 0, 0, EPI)->getAs<FunctionProtoType>();
+
+  if (CtorType->hasExceptionSpec()) {
+    if (CheckEquivalentExceptionSpec(
+          PDiag(diag::err_incorrect_defaulted_exception_spec),
+          PDiag(),
+          ExceptionType, SourceLocation(),
+          CtorType, CD->getLocation())) {
+      CD->setInvalidDecl();
+      return;
+    }
+  } else if (First) {
+    // We set the declaration to have the computed exception spec here.
+    // We know there are no parameters.
+    CD->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
+  }
+
 }
 
 /// \brief Data used with FindHiddenVirtualMethod
@@ -3053,113 +3174,6 @@
                         dyn_cast_or_null<CXXRecordDecl>(TagDecl));
 }
 
-namespace {
-  /// \brief Helper class that collects exception specifications for 
-  /// implicitly-declared special member functions.
-  class ImplicitExceptionSpecification {
-    ASTContext &Context;
-    // We order exception specifications thus:
-    // noexcept is the most restrictive, but is only used in C++0x.
-    // throw() comes next.
-    // Then a throw(collected exceptions)
-    // Finally no specification.
-    // throw(...) is used instead if any called function uses it.
-    ExceptionSpecificationType ComputedEST;
-    llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
-    llvm::SmallVector<QualType, 4> Exceptions;
-
-    void ClearExceptions() {
-      ExceptionsSeen.clear();
-      Exceptions.clear();
-    }
-
-  public:
-    explicit ImplicitExceptionSpecification(ASTContext &Context) 
-      : Context(Context), ComputedEST(EST_BasicNoexcept) {
-      if (!Context.getLangOptions().CPlusPlus0x)
-        ComputedEST = EST_DynamicNone;
-    }
-
-    /// \brief Get the computed exception specification type.
-    ExceptionSpecificationType getExceptionSpecType() const {
-      assert(ComputedEST != EST_ComputedNoexcept &&
-             "noexcept(expr) should not be a possible result");
-      return ComputedEST;
-    }
-
-    /// \brief The number of exceptions in the exception specification.
-    unsigned size() const { return Exceptions.size(); }
-
-    /// \brief The set of exceptions in the exception specification.
-    const QualType *data() const { return Exceptions.data(); }
-
-    /// \brief Integrate another called method into the collected data.
-    void CalledDecl(CXXMethodDecl *Method) {
-      // If we have an MSAny spec already, don't bother.
-      if (!Method || ComputedEST == EST_MSAny)
-        return;
-
-      const FunctionProtoType *Proto
-        = Method->getType()->getAs<FunctionProtoType>();
-
-      ExceptionSpecificationType EST = Proto->getExceptionSpecType();
-
-      // If this function can throw any exceptions, make a note of that.
-      if (EST == EST_MSAny || EST == EST_None) {
-        ClearExceptions();
-        ComputedEST = EST;
-        return;
-      }
-
-      // If this function has a basic noexcept, it doesn't affect the outcome.
-      if (EST == EST_BasicNoexcept)
-        return;
-
-      // If we have a throw-all spec at this point, ignore the function.
-      if (ComputedEST == EST_None)
-        return;
-
-      // If we're still at noexcept(true) and there's a nothrow() callee,
-      // change to that specification.
-      if (EST == EST_DynamicNone) {
-        if (ComputedEST == EST_BasicNoexcept)
-          ComputedEST = EST_DynamicNone;
-        return;
-      }
-
-      // Check out noexcept specs.
-      if (EST == EST_ComputedNoexcept) {
-        FunctionProtoType::NoexceptResult NR = Proto->getNoexceptSpec(Context);
-        assert(NR != FunctionProtoType::NR_NoNoexcept &&
-               "Must have noexcept result for EST_ComputedNoexcept.");
-        assert(NR != FunctionProtoType::NR_Dependent &&
-               "Should not generate implicit declarations for dependent cases, "
-               "and don't know how to handle them anyway.");
-
-        // noexcept(false) -> no spec on the new function
-        if (NR == FunctionProtoType::NR_Throw) {
-          ClearExceptions();
-          ComputedEST = EST_None;
-        }
-        // noexcept(true) won't change anything either.
-        return;
-      }
-
-      assert(EST == EST_Dynamic && "EST case not considered earlier.");
-      assert(ComputedEST != EST_None &&
-             "Shouldn't collect exceptions when throw-all is guaranteed.");
-      ComputedEST = EST_Dynamic;
-      // Record the exceptions in this function's exception specification.
-      for (FunctionProtoType::exception_iterator E = Proto->exception_begin(),
-                                              EEnd = Proto->exception_end();
-           E != EEnd; ++E)
-        if (ExceptionsSeen.insert(Context.getCanonicalType(*E)))
-          Exceptions.push_back(*E);
-    }
-  };
-}
-
-
 /// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared
 /// special functions, such as the default constructor, copy
 /// constructor, or destructor, to the given C++ class (C++
@@ -4980,17 +4994,8 @@
   return 0;
 }
 
-CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
-                                                     CXXRecordDecl *ClassDecl) {
-  // C++ [class.ctor]p5:
-  //   A default constructor for a class X is a constructor of class X
-  //   that can be called without an argument. If there is no
-  //   user-declared constructor for class X, a default constructor is
-  //   implicitly declared. An implicitly-declared default constructor
-  //   is an inline public member of its class.
-  assert(!ClassDecl->hasUserDeclaredConstructor() && 
-         "Should not build implicit default constructor!");
-  
+Sema::ImplicitExceptionSpecification
+Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) {
   // C++ [except.spec]p14:
   //   An implicitly declared special member function (Clause 12) shall have an 
   //   exception-specification. [...]
@@ -5043,10 +5048,23 @@
     }
   }
 
-  FunctionProtoType::ExtProtoInfo EPI;
-  EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
-  EPI.NumExceptions = ExceptSpec.size();
-  EPI.Exceptions = ExceptSpec.data();
+  return ExceptSpec;
+}
+
+CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
+                                                     CXXRecordDecl *ClassDecl) {
+  // C++ [class.ctor]p5:
+  //   A default constructor for a class X is a constructor of class X
+  //   that can be called without an argument. If there is no
+  //   user-declared constructor for class X, a default constructor is
+  //   implicitly declared. An implicitly-declared default constructor
+  //   is an inline public member of its class.
+  assert(!ClassDecl->hasUserDeclaredConstructor() && 
+         "Should not build implicit default constructor!");
+
+  ImplicitExceptionSpecification Spec = 
+    ComputeDefaultedDefaultCtorExceptionSpec(ClassDecl);
+  FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI();
 
   // Create the actual constructor declaration.
   CanQualType ClassType





More information about the cfe-commits mailing list