]> granicus.if.org Git - clang/commitdiff
PR14558: Compute triviality of special members (etc) at the end of the class
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 11 Dec 2012 01:14:52 +0000 (01:14 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 11 Dec 2012 01:14:52 +0000 (01:14 +0000)
definition, rather than at the end of the definition of the set of nested
classes. We still defer checking of the user-specified exception specification
to the end of the nesting -- we can't check that until we've parsed the
in-class initializers for non-static data members.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@169805 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/CXX/special/class.ctor/p5-0x.cpp
unittests/AST/DeclPrinterTest.cpp

index 16360e5e8cd8d183d72fdc922c6c37f4646fb714..047804ae4f6f9b2aecf11836d698eb136a88bcf9 100644 (file)
@@ -366,6 +366,16 @@ public:
                               const CXXDestructorDecl*>, 2>
       DelayedDestructorExceptionSpecChecks;
 
+  /// \brief All the members seen during a class definition which were both
+  /// explicitly defaulted and had explicitly-specified exception
+  /// specifications, along with the function type containing their
+  /// user-specified exception specification. Those exception specifications
+  /// were overridden with the default specifications, but we still need to
+  /// check whether they are compatible with the default specification, and
+  /// we can't do that until the nesting set of class definitions is complete.
+  SmallVector<std::pair<CXXMethodDecl*, const FunctionProtoType*>, 2>
+    DelayedDefaultedMemberExceptionSpecs;
+
   /// \brief Callback to the parser to parse templated functions when needed.
   typedef void LateTemplateParserCB(void *P, const FunctionDecl *FD);
   LateTemplateParserCB *LateTemplateParser;
@@ -4441,8 +4451,10 @@ public:
                                  StorageClass& SC);
   Decl *ActOnConversionDeclarator(CXXConversionDecl *Conversion);
 
-  void CheckExplicitlyDefaultedAndDeletedMethods(CXXRecordDecl *Record);
   void CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD);
+  void CheckExplicitlyDefaultedMemberExceptionSpec(CXXMethodDecl *MD,
+                                                   const FunctionProtoType *T);
+  void CheckDelayedExplicitlyDefaultedMemberExceptionSpecs();
 
   //===--------------------------------------------------------------------===//
   // C++ Derived Classes
index 2fae8c9f91bb1811726f747446a5e4988adaa563..027491cbbe41174aa2c66f912e07667aec698d56 100644 (file)
@@ -3981,14 +3981,69 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
     DiagnoseAbstractType(Record);
   }
 
-  // See if a method overloads virtual methods in a base
-  /// class without overriding any.
   if (!Record->isDependentType()) {
     for (CXXRecordDecl::method_iterator M = Record->method_begin(),
                                      MEnd = Record->method_end();
          M != MEnd; ++M) {
+      // See if a method overloads virtual methods in a base
+      // class without overriding any.
       if (!M->isStatic())
         DiagnoseHiddenVirtualMethods(Record, *M);
+
+      // Check whether the explicitly-defaulted special members are valid.
+      if (!M->isInvalidDecl() && M->isExplicitlyDefaulted())
+        CheckExplicitlyDefaultedSpecialMember(*M);
+
+      // For an explicitly defaulted or deleted special member, we defer
+      // determining triviality until the class is complete. That time is now!
+      if (!M->isImplicit() && !M->isUserProvided()) {
+        CXXSpecialMember CSM = getSpecialMember(*M);
+        if (CSM != CXXInvalid) {
+          M->setTrivial(SpecialMemberIsTrivial(*M, CSM));
+
+          // Inform the class that we've finished declaring this member.
+          Record->finishedDefaultedOrDeletedMember(*M);
+        }
+      }
+    }
+  }
+
+  // C++11 [dcl.constexpr]p8: A constexpr specifier for a non-static member
+  // function that is not a constructor declares that member function to be
+  // const. [...] The class of which that function is a member shall be
+  // a literal type.
+  //
+  // If the class has virtual bases, any constexpr members will already have
+  // been diagnosed by the checks performed on the member declaration, so
+  // suppress this (less useful) diagnostic.
+  //
+  // We delay this until we know whether an explicitly-defaulted (or deleted)
+  // destructor for the class is trivial.
+  if (LangOpts.CPlusPlus0x && !Record->isDependentType() &&
+      !Record->isLiteral() && !Record->getNumVBases()) {
+    for (CXXRecordDecl::method_iterator M = Record->method_begin(),
+                                     MEnd = Record->method_end();
+         M != MEnd; ++M) {
+      if (M->isConstexpr() && M->isInstance() && !isa<CXXConstructorDecl>(*M)) {
+        switch (Record->getTemplateSpecializationKind()) {
+        case TSK_ImplicitInstantiation:
+        case TSK_ExplicitInstantiationDeclaration:
+        case TSK_ExplicitInstantiationDefinition:
+          // If a template instantiates to a non-literal type, but its members
+          // instantiate to constexpr functions, the template is technically
+          // ill-formed, but we allow it for sanity.
+          continue;
+
+        case TSK_Undeclared:
+        case TSK_ExplicitSpecialization:
+          RequireLiteralType(M->getLocation(), Context.getRecordType(Record),
+                             diag::err_constexpr_method_non_literal);
+          break;
+        }
+
+        // Only produce one error per class.
+        break;
+      }
     }
   }
 
@@ -4002,27 +4057,6 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
   DeclareInheritedConstructors(Record);
 }
 
-void Sema::CheckExplicitlyDefaultedAndDeletedMethods(CXXRecordDecl *Record) {
-  for (CXXRecordDecl::method_iterator MI = Record->method_begin(),
-                                      ME = Record->method_end();
-       MI != ME; ++MI) {
-    if (!MI->isInvalidDecl() && MI->isExplicitlyDefaulted())
-      CheckExplicitlyDefaultedSpecialMember(*MI);
-
-    if (!MI->isImplicit() && !MI->isUserProvided()) {
-      // For an explicitly defaulted or deleted special member, we defer
-      // determining triviality until the class is complete. That time is now!
-      CXXSpecialMember CSM = getSpecialMember(*MI);
-      if (CSM != CXXInvalid) {
-        MI->setTrivial(SpecialMemberIsTrivial(*MI, CSM));
-
-        // Inform the class that we've finished declaring this member.
-        Record->finishedDefaultedOrDeletedMember(*MI);
-      }
-    }
-  }
-}
-
 /// Is the special member function which would be selected to perform the
 /// specified operation on the specified class type a constexpr constructor?
 static bool specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
@@ -4271,16 +4305,6 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
     HadError = true;
   }
 
-  // Rebuild the type with the implicit exception specification added, if we
-  // are going to need it.
-  const FunctionProtoType *ImplicitType = 0;
-  if (First || Type->hasExceptionSpec()) {
-    FunctionProtoType::ExtProtoInfo EPI = Type->getExtProtoInfo();
-    computeImplicitExceptionSpec(*this, MD->getLocation(), MD).getEPI(EPI);
-    ImplicitType = cast<FunctionProtoType>(
-      Context.getFunctionType(ReturnType, &ArgType, ExpectedParams, EPI));
-  }
-
   // C++11 [dcl.fct.def.default]p2:
   //   An explicitly-defaulted function may be declared constexpr only if it
   //   would have been implicitly declared as constexpr,
@@ -4295,13 +4319,17 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
     // FIXME: Explain why the constructor can't be constexpr.
     HadError = true;
   }
+
   //   and may have an explicit exception-specification only if it is compatible
   //   with the exception-specification on the implicit declaration.
-  if (Type->hasExceptionSpec() &&
-      CheckEquivalentExceptionSpec(
-        PDiag(diag::err_incorrect_defaulted_exception_spec) << CSM,
-        PDiag(), ImplicitType, SourceLocation(), Type, MD->getLocation()))
-    HadError = true;
+  if (Type->hasExceptionSpec()) {
+    // Delay the check if this is the first declaration of the special member,
+    // since we may not have parsed some necessary in-class initializers yet.
+    if (First)
+      DelayedDefaultedMemberExceptionSpecs.push_back(std::make_pair(MD, Type));
+    else
+      CheckExplicitlyDefaultedMemberExceptionSpec(MD, Type);
+  }
 
   //   If a function is explicitly defaulted on its first declaration,
   if (First) {
@@ -4311,7 +4339,11 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
 
     //  -- it is implicitly considered to have the same exception-specification
     //     as if it had been implicitly declared,
-    MD->setType(QualType(ImplicitType, 0));
+    FunctionProtoType::ExtProtoInfo EPI = Type->getExtProtoInfo();
+    EPI.ExceptionSpecType = EST_Unevaluated;
+    EPI.ExceptionSpecDecl = MD;
+    MD->setType(Context.getFunctionType(ReturnType, &ArgType,
+                                        ExpectedParams, EPI));
   }
 
   if (ShouldDeleteSpecialMember(MD, CSM)) {
@@ -4330,6 +4362,36 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
     MD->setInvalidDecl();
 }
 
+/// Check whether the exception specification provided for an
+/// explicitly-defaulted special member matches the exception specification
+/// that would have been generated for an implicit special member, per
+/// C++11 [dcl.fct.def.default]p2.
+void Sema::CheckExplicitlyDefaultedMemberExceptionSpec(
+    CXXMethodDecl *MD, const FunctionProtoType *SpecifiedType) {
+  // Compute the implicit exception specification.
+  FunctionProtoType::ExtProtoInfo EPI;
+  computeImplicitExceptionSpec(*this, MD->getLocation(), MD).getEPI(EPI);
+  const FunctionProtoType *ImplicitType = cast<FunctionProtoType>(
+    Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
+
+  // Ensure that it matches.
+  CheckEquivalentExceptionSpec(
+    PDiag(diag::err_incorrect_defaulted_exception_spec)
+      << getSpecialMember(MD), PDiag(),
+    ImplicitType, SourceLocation(),
+    SpecifiedType, MD->getLocation());
+}
+
+void Sema::CheckDelayedExplicitlyDefaultedMemberExceptionSpecs() {
+  for (unsigned I = 0, N = DelayedDefaultedMemberExceptionSpecs.size();
+       I != N; ++I)
+    CheckExplicitlyDefaultedMemberExceptionSpec(
+      DelayedDefaultedMemberExceptionSpecs[I].first,
+      DelayedDefaultedMemberExceptionSpecs[I].second);
+
+  DelayedDefaultedMemberExceptionSpecs.clear();
+}
+
 namespace {
 struct SpecialMemberDeletionInfo {
   Sema &S;
@@ -7500,52 +7562,9 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,
 }
 
 void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) {
-  if (!D) return;
-  AdjustDeclIfTemplate(D);
-
-  CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(D);
-
-  if (!ClassDecl->isDependentType())
-    CheckExplicitlyDefaultedAndDeletedMethods(ClassDecl);
-
-  // C++11 [dcl.constexpr]p8: A constexpr specifier for a non-static member
-  // function that is not a constructor declares that member function to be
-  // const. [...] The class of which that function is a member shall be
-  // a literal type.
-  //
-  // If the class has virtual bases, any constexpr members will already have
-  // been diagnosed by the checks performed on the member declaration, so
-  // suppress this (less useful) diagnostic.
-  //
-  // We delay this until we know whether an explicitly-defaulted (or deleted)
-  // destructor for the class is trivial.
-  if (LangOpts.CPlusPlus0x && !ClassDecl->isDependentType() &&
-      !ClassDecl->isLiteral() && !ClassDecl->getNumVBases()) {
-    for (CXXRecordDecl::method_iterator M = ClassDecl->method_begin(),
-                                     MEnd = ClassDecl->method_end();
-         M != MEnd; ++M) {
-      if (M->isConstexpr() && M->isInstance() && !isa<CXXConstructorDecl>(*M)) {
-        switch (ClassDecl->getTemplateSpecializationKind()) {
-        case TSK_ImplicitInstantiation:
-        case TSK_ExplicitInstantiationDeclaration:
-        case TSK_ExplicitInstantiationDefinition:
-          // If a template instantiates to a non-literal type, but its members
-          // instantiate to constexpr functions, the template is technically
-          // ill-formed, but we allow it for sanity.
-          continue;
-
-        case TSK_Undeclared:
-        case TSK_ExplicitSpecialization:
-          RequireLiteralType(M->getLocation(), Context.getRecordType(ClassDecl),
-                             diag::err_constexpr_method_non_literal);
-          break;
-        }
-
-        // Only produce one error per class.
-        break;
-      }
-    }
-  }
+  // Check that any explicitly-defaulted methods have exception specifications
+  // compatible with their implicit exception specifications.
+  CheckDelayedExplicitlyDefaultedMemberExceptionSpecs();
 }
 
 void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
@@ -10882,6 +10901,11 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
 
     CheckExplicitlyDefaultedSpecialMember(MD);
 
+    // The exception specification is needed because we are defining the
+    // function.
+    ResolveExceptionSpec(DefaultLoc,
+                         MD->getType()->castAs<FunctionProtoType>());
+
     switch (Member) {
     case CXXDefaultConstructor: {
       CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD);
index 337534eaac351549f508b0098aeaa8ad1ee3f92b..566f6eef7fab7f43be1b0e8ea6475b369c331889 100644 (file)
@@ -2782,7 +2782,6 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
 
   EnterExpressionEvaluationContext EvalContext(*this,
                                                Sema::PotentiallyEvaluated);
-  ActOnStartOfFunctionDef(0, Function);
 
   // Introduce a new scope where local variable instantiations will be
   // recorded, unless we're actually a member function within a local
@@ -2794,21 +2793,21 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
 
   LocalInstantiationScope Scope(*this, MergeWithParentScope);
 
-  // Enter the scope of this instantiation. We don't use
-  // PushDeclContext because we don't have a scope.
-  Sema::ContextRAII savedContext(*this, Function);
+  if (PatternDecl->isDefaulted())
+    SetDeclDefaulted(Function, PatternDecl->getLocation());
+  else {
+    ActOnStartOfFunctionDef(0, Function);
 
-  MultiLevelTemplateArgumentList TemplateArgs =
-    getTemplateInstantiationArgs(Function, 0, false, PatternDecl);
+    // Enter the scope of this instantiation. We don't use
+    // PushDeclContext because we don't have a scope.
+    Sema::ContextRAII savedContext(*this, Function);
 
-  addInstantiatedParametersToScope(*this, Function, PatternDecl, Scope,
-                                   TemplateArgs);
+    MultiLevelTemplateArgumentList TemplateArgs =
+      getTemplateInstantiationArgs(Function, 0, false, PatternDecl);
 
-  if (PatternDecl->isDefaulted()) {
-    ActOnFinishFunctionBody(Function, 0, /*IsInstantiation=*/true);
+    addInstantiatedParametersToScope(*this, Function, PatternDecl, Scope,
+                                     TemplateArgs);
 
-    SetDeclDefaulted(Function, PatternDecl->getLocation());
-  } else {
     // If this is a constructor, instantiate the member initializers.
     if (const CXXConstructorDecl *Ctor =
           dyn_cast<CXXConstructorDecl>(PatternDecl)) {
@@ -2824,11 +2823,11 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
 
     ActOnFinishFunctionBody(Function, Body.get(),
                             /*IsInstantiation=*/true);
-  }
 
-  PerformDependentDiagnostics(PatternDecl, TemplateArgs);
+    PerformDependentDiagnostics(PatternDecl, TemplateArgs);
 
-  savedContext.pop();
+    savedContext.pop();
+  }
 
   DeclGroupRef DG(Function);
   Consumer.HandleTopLevelDecl(DG);
index ab8fdb0f1bd9e04c127c56d210d3c9cc1ceee47e..0f4add8c97423057000ad82d3ebcbe7534928987 100644 (file)
@@ -195,3 +195,15 @@ static_assert(__has_trivial_constructor(Trivial4<int>), "Trivial4 is trivial");
 
 template<typename T> class Trivial5 { Trivial5() = delete; };
 static_assert(__has_trivial_constructor(Trivial5<int>), "Trivial5 is trivial");
+
+namespace PR14558 {
+  // Ensure we determine whether an explicitly-defaulted or deleted special
+  // member is trivial before we return to parsing the containing class.
+  struct A {
+    struct B { B() = default; } b;
+    struct C { C() = delete; } c;
+  };
+
+  static_assert(__has_trivial_constructor(A), "");
+  static_assert(__has_trivial_constructor(A::B), "");
+}
index 50aab032120823b65b739c6d59a78f089457b05f..844b9a49e13adfbe077a30d5da91285f258fb6cd 100644 (file)
@@ -476,8 +476,7 @@ TEST(DeclPrinter, TestCXXConstructorDecl8) {
     "  A() = default;"
     "};",
     constructorDecl(ofClass(hasName("A"))).bind("id"),
-    "A() noexcept = default"));
-    // Should be: "A() = default;" if we care about noexcept as written
+    "A() = default"));
 }
 
 TEST(DeclPrinter, TestCXXConstructorDecl9) {