]> granicus.if.org Git - clang/commitdiff
PR38627: Fix handling of exception specification adjustment for
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 5 Sep 2018 22:30:37 +0000 (22:30 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 5 Sep 2018 22:30:37 +0000 (22:30 +0000)
destructors.

We previously tried to patch up the exception specification after
completing the class, which went wrong when the exception specification
was needed within the class body (in particular, by a friend
redeclaration of the destructor in a nested class). We now mark the
destructor as having a not-yet-computed exception specification
immediately after creating it.

This requires delaying various checks against the exception
specification (where we'd previously have just got the wrong exception
specification, and now find we have an exception specification that we
can't compute yet) when those checks fire while the class is being
defined.

This also exposed an issue that we were missing a CodeSynthesisContext
for computation of exception specifications (otherwise we'd fail to make
the module containing the definition of the class visible when computing
its members' exception specs). Adding that incidentally also gives us a
diagnostic quality improvement.

This has also exposed an pre-existing problem: making the exception
specification evaluation context a non-SFINAE context (as it should be)
results in a bootstrap failure; PR38850 filed for this.

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

19 files changed:
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Frontend/FrontendActions.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExceptionSpec.cpp
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/CXX/drs/dr13xx.cpp
test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp
test/Modules/cxx-templates.cpp
test/Modules/exception-spec.cpp [new file with mode: 0644]
test/SemaCXX/constant-expression-cxx11.cpp
test/SemaCXX/cxx0x-defaulted-functions.cpp
test/SemaCXX/exception-spec.cpp
test/SemaCXX/implicit-exception-spec.cpp
test/SemaCXX/member-init.cpp
test/SemaTemplate/instantiate-init.cpp

index 6db9bbea279b593516f1ecc44206f822fc6b3b61..c1f0774d63acacdb43a5534cd8904f11b43c0cd7 100644 (file)
@@ -1467,6 +1467,10 @@ def err_noexcept_needs_constant_expression : Error<
   "argument to noexcept specifier must be a constant expression">;
 def err_exception_spec_not_parsed : Error<
   "exception specification is not available until end of class definition">;
+def err_exception_spec_cycle : Error<
+  "exception specification of %0 uses itself">;
+def err_exception_spec_incomplete_type : Error<
+  "exception specification needed for member of incomplete class %0">;
 
 // C++ access checking
 def err_class_redeclared_with_different_access : Error<
@@ -4270,6 +4274,8 @@ def note_forward_template_decl : Note<
 def note_inst_declaration_hint : Note<"add an explicit instantiation "
   "declaration to suppress this warning if %q0 is explicitly instantiated in "
   "another translation unit">;
+def note_evaluating_exception_spec_here : Note<
+  "in evaluation of exception specification for %q0 needed here">;
 
 def note_default_arg_instantiation_here : Note<
   "in instantiation of default argument for '%0' required here">;
@@ -7471,8 +7477,6 @@ def note_in_class_initializer_not_yet_parsed : Note<
   "default member initializer declared here">;
 def err_in_class_initializer_cycle
     : Error<"default member initializer for %0 uses itself">;
-def err_exception_spec_cycle
-    : Error<"exception specification of %0 uses itself">;
 
 def ext_in_class_initializer_non_constant : Extension<
   "in-class initializer for static data member is not a constant expression; "
index 5f6cc515697bc8f8e1ad7d3a51c6ada5748ece2c..465e8cddb5af4256313e02081f2e81e0f3158ba4 100644 (file)
@@ -608,7 +608,15 @@ public:
   /// that had their exception spec checks delayed, plus the overridden
   /// function.
   SmallVector<std::pair<const CXXMethodDecl*, const CXXMethodDecl*>, 2>
-    DelayedExceptionSpecChecks;
+    DelayedOverridingExceptionSpecChecks;
+
+  /// All the function redeclarations seen during a class definition that had
+  /// their exception spec checks delayed, plus the prior declaration they
+  /// should be checked against. Except during error recovery, the new decl
+  /// should always be a friend declaration, as that's the only valid way to
+  /// redeclare a special member before its class is complete.
+  SmallVector<std::pair<FunctionDecl*, FunctionDecl*>, 2>
+    DelayedEquivalentExceptionSpecChecks;
 
   /// All the members seen during a class definition which were both
   /// explicitly defaulted and had explicitly-specified exception
@@ -4872,8 +4880,7 @@ public:
   ///
   /// C++11 says that user-defined destructors with no exception spec get one
   /// that looks as if the destructor was implicitly declared.
-  void AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl,
-                                     CXXDestructorDecl *Destructor);
+  void AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor);
 
   /// Define the specified inheriting constructor.
   void DefineInheritingConstructor(SourceLocation UseLoc,
@@ -7142,6 +7149,10 @@ public:
       /// has been used when naming a template-id.
       DefaultTemplateArgumentChecking,
 
+      /// We are computing the exception specification for a defaulted special
+      /// member function.
+      ExceptionSpecEvaluation,
+
       /// We are instantiating the exception specification for a function
       /// template which was deferred until it was needed.
       ExceptionSpecInstantiation,
@@ -10635,7 +10646,9 @@ private:
     SavePendingParsedClassStateRAII(Sema &S) : S(S) { swapSavedState(); }
 
     ~SavePendingParsedClassStateRAII() {
-      assert(S.DelayedExceptionSpecChecks.empty() &&
+      assert(S.DelayedOverridingExceptionSpecChecks.empty() &&
+             "there shouldn't be any pending delayed exception spec checks");
+      assert(S.DelayedEquivalentExceptionSpecChecks.empty() &&
              "there shouldn't be any pending delayed exception spec checks");
       assert(S.DelayedDefaultedMemberExceptionSpecs.empty() &&
              "there shouldn't be any pending delayed defaulted member "
@@ -10647,13 +10660,19 @@ private:
 
   private:
     Sema &S;
-    decltype(DelayedExceptionSpecChecks) SavedExceptionSpecChecks;
+    decltype(DelayedOverridingExceptionSpecChecks)
+        SavedOverridingExceptionSpecChecks;
+    decltype(DelayedEquivalentExceptionSpecChecks)
+        SavedEquivalentExceptionSpecChecks;
     decltype(DelayedDefaultedMemberExceptionSpecs)
         SavedDefaultedMemberExceptionSpecs;
     decltype(DelayedDllExportClasses) SavedDllExportClasses;
 
     void swapSavedState() {
-      SavedExceptionSpecChecks.swap(S.DelayedExceptionSpecChecks);
+      SavedOverridingExceptionSpecChecks.swap(
+          S.DelayedOverridingExceptionSpecChecks);
+      SavedEquivalentExceptionSpecChecks.swap(
+          S.DelayedEquivalentExceptionSpecChecks);
       SavedDefaultedMemberExceptionSpecs.swap(
           S.DelayedDefaultedMemberExceptionSpecs);
       SavedDllExportClasses.swap(S.DelayedDllExportClasses);
index 8a8354c7d4c90c03502225318bf84511387d4497..342081eba672ebce75d0c878ae27f6eccf2342d5 100644 (file)
@@ -341,6 +341,8 @@ private:
       return "PriorTemplateArgumentSubstitution";
     case CodeSynthesisContext::DefaultTemplateArgumentChecking:
       return "DefaultTemplateArgumentChecking";
+    case CodeSynthesisContext::ExceptionSpecEvaluation:
+      return "ExceptionSpecEvaluation";
     case CodeSynthesisContext::ExceptionSpecInstantiation:
       return "ExceptionSpecInstantiation";
     case CodeSynthesisContext::DeclaringSpecialMember:
index bd2637a72ef26af66e297c54e5373d5186ca150e..82a04e1070d227f8b31cab35d363b60415c9a35f 100644 (file)
@@ -922,10 +922,9 @@ void Sema::ActOnEndOfTranslationUnit() {
 
   // All delayed member exception specs should be checked or we end up accepting
   // incompatible declarations.
-  // FIXME: This is wrong for TUKind == TU_Prefix. In that case, we need to
-  // write out the lists to the AST file (if any).
+  assert(DelayedOverridingExceptionSpecChecks.empty());
+  assert(DelayedEquivalentExceptionSpecChecks.empty());
   assert(DelayedDefaultedMemberExceptionSpecs.empty());
-  assert(DelayedExceptionSpecChecks.empty());
 
   // All dllexport classes should have been processed already.
   assert(DelayedDllExportClasses.empty());
index e62728c124c7a520a77b3bb31cb030c4dc76b066..c705b4920e6206f045427040dd8ba7a2f9b82d98 100644 (file)
@@ -7956,14 +7956,11 @@ static FunctionDecl* CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
                                     NameInfo, R, TInfo, isInline,
                                     /*isImplicitlyDeclared=*/false);
 
-      // If the class is complete, then we now create the implicit exception
-      // specification. If the class is incomplete or dependent, we can't do
-      // it yet.
-      if (SemaRef.getLangOpts().CPlusPlus11 && !Record->isDependentType() &&
-          Record->getDefinition() && !Record->isBeingDefined() &&
-          R->getAs<FunctionProtoType>()->getExceptionSpecType() == EST_None) {
-        SemaRef.AdjustDestructorExceptionSpec(Record, NewDD);
-      }
+      // If the destructor needs an implicit exception specification, set it
+      // now. FIXME: It'd be nice to be able to create the right type to start
+      // with, but the type needs to reference the destructor declaration.
+      if (SemaRef.getLangOpts().CPlusPlus11)
+        SemaRef.AdjustDestructorExceptionSpec(NewDD);
 
       IsVirtualOkay = true;
       return NewDD;
@@ -15810,13 +15807,6 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
       }
 
       if (!CXXRecord->isDependentType()) {
-        if (CXXRecord->hasUserDeclaredDestructor()) {
-          // Adjust user-defined destructor exception spec.
-          if (getLangOpts().CPlusPlus11)
-            AdjustDestructorExceptionSpec(CXXRecord,
-                                          CXXRecord->getDestructor());
-        }
-
         // Add any implicitly-declared members to this class.
         AddImplicitlyDeclaredMembersToClass(CXXRecord);
 
index 61a7730d9b0aee66a7b90cd31b96c8a53b823187..d3f7af25295a535af217ba1194cbb4eb553b8ca3 100644 (file)
@@ -6635,20 +6635,27 @@ void Sema::CheckExplicitlyDefaultedMemberExceptionSpec(
 }
 
 void Sema::CheckDelayedMemberExceptionSpecs() {
-  decltype(DelayedExceptionSpecChecks) Checks;
-  decltype(DelayedDefaultedMemberExceptionSpecs) Specs;
+  decltype(DelayedOverridingExceptionSpecChecks) Overriding;
+  decltype(DelayedEquivalentExceptionSpecChecks) Equivalent;
+  decltype(DelayedDefaultedMemberExceptionSpecs) Defaulted;
 
-  std::swap(Checks, DelayedExceptionSpecChecks);
-  std::swap(Specs, DelayedDefaultedMemberExceptionSpecs);
+  std::swap(Overriding, DelayedOverridingExceptionSpecChecks);
+  std::swap(Equivalent, DelayedEquivalentExceptionSpecChecks);
+  std::swap(Defaulted, DelayedDefaultedMemberExceptionSpecs);
 
   // Perform any deferred checking of exception specifications for virtual
   // destructors.
-  for (auto &Check : Checks)
+  for (auto &Check : Overriding)
     CheckOverridingFunctionExceptionSpec(Check.first, Check.second);
 
+  // Perform any deferred checking of exception specifications for befriended
+  // special members.
+  for (auto &Check : Equivalent)
+    CheckEquivalentExceptionSpec(Check.second, Check.first);
+
   // Check that any explicitly-defaulted methods have exception specifications
   // compatible with their implicit exception specifications.
-  for (auto &Spec : Specs)
+  for (auto &Spec : Defaulted)
     CheckExplicitlyDefaultedMemberExceptionSpec(Spec.first, Spec.second);
 }
 
@@ -10685,19 +10692,48 @@ void SpecialMemberExceptionSpecInfo::visitSubobjectCall(
     ExceptSpec.CalledDecl(getSubobjectLoc(Subobj), MD);
 }
 
+namespace {
+/// RAII object to register a special member as being currently declared.
+struct ComputingExceptionSpec {
+  Sema &S;
+
+  ComputingExceptionSpec(Sema &S, CXXMethodDecl *MD, SourceLocation Loc)
+      : S(S) {
+    Sema::CodeSynthesisContext Ctx;
+    Ctx.Kind = Sema::CodeSynthesisContext::ExceptionSpecEvaluation;
+    Ctx.PointOfInstantiation = Loc;
+    Ctx.Entity = MD;
+    S.pushCodeSynthesisContext(Ctx);
+  }
+  ~ComputingExceptionSpec() {
+    S.popCodeSynthesisContext();
+  }
+};
+}
+
 static Sema::ImplicitExceptionSpecification
 ComputeDefaultedSpecialMemberExceptionSpec(
     Sema &S, SourceLocation Loc, CXXMethodDecl *MD, Sema::CXXSpecialMember CSM,
     Sema::InheritedConstructorInfo *ICI) {
+  ComputingExceptionSpec CES(S, MD, Loc);
+
   CXXRecordDecl *ClassDecl = MD->getParent();
 
   // C++ [except.spec]p14:
   //   An implicitly declared special member function (Clause 12) shall have an
   //   exception-specification. [...]
-  SpecialMemberExceptionSpecInfo Info(S, MD, CSM, ICI, Loc);
+  SpecialMemberExceptionSpecInfo Info(S, MD, CSM, ICI, MD->getLocation());
   if (ClassDecl->isInvalidDecl())
     return Info.ExceptSpec;
 
+  // FIXME: If this diagnostic fires, we're probably missing a check for
+  // attempting to resolve an exception specification before it's known
+  // at a higher level.
+  if (S.RequireCompleteType(MD->getLocation(),
+                            S.Context.getRecordType(ClassDecl),
+                            diag::err_exception_spec_incomplete_type))
+    return Info.ExceptSpec;
+
   // C++1z [except.spec]p7:
   //   [Look for exceptions thrown by] a constructor selected [...] to
   //   initialize a potentially constructed subobject,
@@ -11172,8 +11208,9 @@ void Sema::ActOnFinishCXXMemberDecls() {
   // If the context is an invalid C++ class, just suppress these checks.
   if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(CurContext)) {
     if (Record->isInvalidDecl()) {
+      DelayedOverridingExceptionSpecChecks.clear();
+      DelayedEquivalentExceptionSpecChecks.clear();
       DelayedDefaultedMemberExceptionSpecs.clear();
-      DelayedExceptionSpecChecks.clear();
       return;
     }
     checkForMultipleExportedDefaultConstructors(*this, Record);
@@ -11195,11 +11232,13 @@ void Sema::referenceDLLExportedClassMethods() {
   }
 }
 
-void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl,
-                                         CXXDestructorDecl *Destructor) {
+void Sema::AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor) {
   assert(getLangOpts().CPlusPlus11 &&
          "adjusting dtor exception specs was introduced in c++11");
 
+  if (Destructor->isDependentContext())
+    return;
+
   // C++11 [class.dtor]p3:
   //   A declaration of a destructor that does not have an exception-
   //   specification is implicitly considered to have the same exception-
index ec9dab8f53a48a29a3bc1109429d48f16ea8d4b8..f3e1563c3a8dbc9928288bdd68adb2c4702f3510 100644 (file)
@@ -230,6 +230,16 @@ Sema::UpdateExceptionSpec(FunctionDecl *FD,
     Context.adjustExceptionSpec(Redecl, ESI);
 }
 
+static bool exceptionSpecNotKnownYet(const FunctionDecl *FD) {
+  auto *MD = dyn_cast<CXXMethodDecl>(FD);
+  if (!MD)
+    return false;
+
+  auto EST = MD->getType()->castAs<FunctionProtoType>()->getExceptionSpecType();
+  return EST == EST_Unparsed ||
+         (EST == EST_Unevaluated && MD->getParent()->isBeingDefined());
+};
+
 static bool CheckEquivalentExceptionSpecImpl(
     Sema &S, const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID,
     const FunctionProtoType *Old, SourceLocation OldLoc,
@@ -278,6 +288,14 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
     ReturnValueOnError = false;
   }
 
+  // If we're befriending a member function of a class that's currently being
+  // defined, we might not be able to work out its exception specification yet.
+  // If not, defer the check until later.
+  if (exceptionSpecNotKnownYet(Old) || exceptionSpecNotKnownYet(New)) {
+    DelayedEquivalentExceptionSpecChecks.push_back({New, Old});
+    return false;
+  }
+
   // Check the types as written: they must match before any exception
   // specification adjustment is applied.
   if (!CheckEquivalentExceptionSpecImpl(
@@ -904,26 +922,21 @@ bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
   if (New->getType()->castAs<FunctionProtoType>()->getExceptionSpecType() ==
       EST_Unparsed)
     return false;
-  if (getLangOpts().CPlusPlus11 && isa<CXXDestructorDecl>(New)) {
-    // Don't check uninstantiated template destructors at all. We can only
-    // synthesize correct specs after the template is instantiated.
-    if (New->getParent()->isDependentType())
-      return false;
-    if (New->getParent()->isBeingDefined()) {
-      // The destructor might be updated once the definition is finished. So
-      // remember it and check later.
-      DelayedExceptionSpecChecks.push_back(std::make_pair(New, Old));
-      return false;
-    }
-  }
-  // If the old exception specification hasn't been parsed yet, remember that
-  // we need to perform this check when we get to the end of the outermost
+
+  // Don't check uninstantiated template destructors at all. We can only
+  // synthesize correct specs after the template is instantiated.
+  if (isa<CXXDestructorDecl>(New) && New->getParent()->isDependentType())
+    return false;
+
+  // If the old exception specification hasn't been parsed yet, or the new
+  // exception specification can't be computed yet, remember that we need to
+  // perform this check when we get to the end of the outermost
   // lexically-surrounding class.
-  if (Old->getType()->castAs<FunctionProtoType>()->getExceptionSpecType() ==
-      EST_Unparsed) {
-    DelayedExceptionSpecChecks.push_back(std::make_pair(New, Old));
+  if (exceptionSpecNotKnownYet(Old) || exceptionSpecNotKnownYet(New)) {
+    DelayedOverridingExceptionSpecChecks.push_back({New, Old});
     return false;
   }
+
   unsigned DiagID = diag::err_override_exception_spec;
   if (getLangOpts().MicrosoftExt)
     DiagID = diag::ext_override_exception_spec;
index 9a5879e67575deeb322f906bb41706e87c84d09e..5666cf04a24e31f6c3b3f39314ac8112fca8a3c3 100644 (file)
@@ -199,6 +199,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
   case DefaultTemplateArgumentChecking:
   case DeclaringSpecialMember:
   case DefiningSynthesizedFunction:
+  case ExceptionSpecEvaluation:
     return false;
 
   // This function should never be called when Kind's value is Memoization.
@@ -621,6 +622,12 @@ void Sema::PrintInstantiationStack() {
       break;
     }
 
+    case CodeSynthesisContext::ExceptionSpecEvaluation:
+      Diags.Report(Active->PointOfInstantiation,
+                   diag::note_evaluating_exception_spec_here)
+          << cast<FunctionDecl>(Active->Entity);
+      break;
+
     case CodeSynthesisContext::ExceptionSpecInstantiation:
       Diags.Report(Active->PointOfInstantiation,
                    diag::note_template_exception_spec_instantiation_here)
@@ -695,6 +702,12 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
       // there is no SFINAE.
       return None;
 
+    case CodeSynthesisContext::ExceptionSpecEvaluation:
+      // FIXME: This should not be treated as a SFINAE context, because
+      // we will cache an incorrect exception specification. However, clang
+      // bootstrap relies this! See PR31692.
+      break;
+
     case CodeSynthesisContext::Memoization:
       break;
     }
index 80980106c4d76880a79277315513f23704878619..3ed9eeed16ef1ab13cd23cd019c680ed10feb7f8 100644 (file)
@@ -3690,6 +3690,9 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New,
   if (InitFunctionInstantiation(New, Tmpl))
     return true;
 
+  if (isa<CXXDestructorDecl>(New) && SemaRef.getLangOpts().CPlusPlus11)
+    SemaRef.AdjustDestructorExceptionSpec(cast<CXXDestructorDecl>(New));
+
   New->setAccess(Tmpl->getAccess());
   if (Tmpl->isVirtualAsWritten())
     New->setVirtualAsWritten(true);
index f193c8e024fbc485c54c7c95af6682344f2e1506..208ab8a03b0a03f4a8abd29d61191fe7eef4cf4c 100644 (file)
@@ -226,10 +226,10 @@ namespace dr1330 { // dr1330: 4 c++11
 #endif
   void f(D &d) { d = d; } // ok
 
-  // FIXME: In C++11 onwards, we should also note the declaration of 'e' as the
-  // line that triggers the use of E::E()'s exception specification.
   struct E : C<int> {}; // expected-note {{in instantiation of}}
-  E e;
+#if __cplusplus >= 201103L
+  E e; // expected-note {{needed here}}
+#endif
 }
 
 namespace dr1346 { // dr1346: 3.5
index 31213c9ebc33439633d01eb28c9a5d8a8db1de6d..57e555e8d9e9f4333509fbdfc6b0a0658a8245f4 100644 (file)
@@ -139,11 +139,11 @@ namespace NonLocalLambdaInstantation {
   }
 
   template<typename T>
-  struct X2 {
+  struct X2 { // expected-note{{in instantiation of default member initializer 'NonLocalLambdaInstantation::X2<int *>::x'}}
     int x = []{ return T(); }(); // expected-error{{cannot initialize a member subobject of type 'int' with an rvalue of type 'int *'}}
   };
 
   X2<int> x2i;
   X2<float> x2f;
-  X2<int*> x2ip; // expected-note{{in instantiation of default member initializer 'NonLocalLambdaInstantation::X2<int *>::x'}}
+  X2<int*> x2ip; // expected-note {{in evaluation of exception spec}}
 }
index 25f8a9992585512c92664280d436d303990ec574..c085eb5f676cc1173422c3ced2d5d915bb218fb7 100644 (file)
@@ -199,6 +199,8 @@ namespace hidden_specializations {
     cls<char*> uk4; // expected-error 1+{{partial specialization of 'cls<T *>' must be imported}} expected-error 1+{{definition of}}
     cls<void>::nested_cls unk1; // expected-error 1+{{explicit specialization of 'nested_cls' must be imported}} expected-error 1+{{definition of}}
     cls<void>::nested_cls_t<int> unk2; // expected-error 1+{{explicit specialization of 'nested_cls_t' must be imported}} expected-error 1+{{definition of}}
+    // expected-error@cxx-templates-unimported.h:29 {{explicit specialization of 'nested_cls_t' must be imported}}
+    // expected-note@-2 {{in evaluation of exception specification}}
     cls<void>::nested_cls_t<char> unk3; // expected-error 1+{{explicit specialization of 'nested_cls_t' must be imported}}
 
     // For enums, uses that would trigger instantiations of definitions are not
diff --git a/test/Modules/exception-spec.cpp b/test/Modules/exception-spec.cpp
new file mode 100644 (file)
index 0000000..083cd95
--- /dev/null
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -x c++ -std=c++17 -fmodules -fmodules-local-submodule-visibility -fmodules-cache-path=%t %s -verify
+
+// expected-no-diagnostics
+
+#pragma clang module build PR38627
+module PR38627 {}
+#pragma clang module contents
+#pragma clang module begin PR38627
+namespace PR38627 {
+struct X {
+  virtual ~X() {}
+  struct C {
+    friend X::~X();
+  } c;
+};
+}
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module import PR38627
+
+namespace PR38627 {
+struct Y {
+  virtual ~Y() {}
+  struct C {
+    friend Y::~Y();
+  } c;
+};
+static_assert(noexcept(X().~X()));
+static_assert(noexcept(Y().~Y()));
+
+struct A { virtual ~A() = default; };
+struct B : public A, public X {
+  virtual ~B() override = default;
+};
+} // PR38627
index a64207583dd5514b24a0e266752712978f16fe3c..c9bfd846415bf2b30d3d31a63c2f7fed8ac1f2f7 100644 (file)
@@ -1972,9 +1972,9 @@ namespace ZeroSizeTypes {
 namespace BadDefaultInit {
   template<int N> struct X { static const int n = N; };
 
-  struct A {
+  struct A { // expected-error {{default member initializer for 'k' needed within definition of enclosing class}}
     int k = // expected-note {{default member initializer declared here}}
-        X<A().k>::n; // expected-error {{default member initializer for 'k' needed within definition of enclosing class}}
+        X<A().k>::n; // expected-note {{in evaluation of exception specification for 'BadDefaultInit::A::A' needed here}}
   };
 
   // FIXME: The "constexpr constructor must initialize all members" diagnostic
index f623dc765f88c0fedc6a11076505d22fbbf37ac7..6346e1c2353848885e1aca531135f79b693677ca 100644 (file)
@@ -99,8 +99,6 @@ namespace DefaultedFnExceptionSpec {
 
   Error<char> c; // expected-note 2{{instantiation of}}
   struct DelayImplicit {
-    // FIXME: The location of this note is terrible. The instantiation was
-    // triggered by the uses of the functions in the decltype expressions below.
     Error<int> e; // expected-note 6{{instantiation of}}
   };
   Error<float> *e;
@@ -109,9 +107,9 @@ namespace DefaultedFnExceptionSpec {
   // a defaulted special member function that calls the function is needed.
   // Use in an unevaluated operand still results in the exception spec being
   // needed.
-  void test1(decltype(declval<DelayImplicit>() = DelayImplicit(DelayImplicit())));
-  void test2(decltype(declval<DelayImplicit>() = declval<const DelayImplicit>()));
-  void test3(decltype(DelayImplicit(declval<const DelayImplicit>())));
+  void test1(decltype(declval<DelayImplicit>() = DelayImplicit(DelayImplicit()))); // expected-note 4{{in evaluation of exception specification}}
+  void test2(decltype(declval<DelayImplicit>() = declval<const DelayImplicit>())); // expected-note {{in evaluation of exception specification}}
+  void test3(decltype(DelayImplicit(declval<const DelayImplicit>()))); // expected-note {{in evaluation of exception specification}}
 
   // Any odr-use needs the exception specification.
   void f(Error<double> *p) {
index f301a63503dd42b26e254eb7fc14c810c081afbb..6ad19aab397bd66dacb7cb68ca7cee1387b5ee0a 100644 (file)
@@ -5,3 +5,50 @@ namespace MissingOnTemplate {
   template<typename T> void foo(T); // expected-error {{missing exception specification 'noexcept(true)'}}
   void test() { foo(0); }
 }
+
+struct UseBeforeComplete1 {
+  ~UseBeforeComplete1(); // expected-note {{previous}}
+  struct X {
+    friend UseBeforeComplete1::~UseBeforeComplete1() noexcept; // expected-warning {{previously declared with an implicit}}
+  };
+};
+
+struct ThrowingDtor { ~ThrowingDtor() noexcept(false); };
+struct UseBeforeComplete2 {
+  ~UseBeforeComplete2(); // expected-note {{previous}}
+  struct X {
+    friend UseBeforeComplete2::~UseBeforeComplete2() noexcept; // expected-error {{does not match previous}}
+  };
+  ThrowingDtor td;
+};
+
+struct UseBeforeComplete3 {
+  ~UseBeforeComplete3();
+  struct X {
+    friend UseBeforeComplete3::~UseBeforeComplete3(); // ok, implicitly noexcept(true)
+  };
+};
+static_assert(noexcept(UseBeforeComplete3()), "");
+
+struct UseBeforeComplete4 {
+  ~UseBeforeComplete4();
+  struct X {
+    friend UseBeforeComplete4::~UseBeforeComplete4(); // ok, implicitly noexcept(false)
+  };
+  ThrowingDtor td;
+};
+static_assert(!noexcept(UseBeforeComplete4()), "");
+
+namespace AssignmentOp {
+  struct D1;
+  struct D2;
+  struct B {
+    B &operator=(const B&);
+    virtual D1 &operator=(const D1&) noexcept; // expected-note {{overridden}}
+    virtual D2 &operator=(const D2&) noexcept; // expected-note {{overridden}}
+  };
+  struct D1 : B {}; // expected-error {{more lax}}
+  struct D2 : B {
+    D2 &operator=(const D2&); // expected-error {{more lax}}
+  };
+}
index c21f773e94c66d1649a09ec5896e1e2300dccbff..19d645d6d3d1549ca10d29770648547c787c7670 100644 (file)
@@ -16,41 +16,39 @@ namespace InClassInitializers {
   // Noexcept::Noexcept is not declared constexpr, therefore noexcept(Noexcept())
   // is false.
   bool ThrowSomething() noexcept(false);
-  struct ConstExpr {
+  struct ConstExpr { // expected-error {{default member initializer for 'b' needed}}
     bool b = // expected-note {{declared here}}
-      noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{default member initializer for 'b' needed}}
+      noexcept(ConstExpr()) && ThrowSomething(); // expected-note {{in evaluation of exception spec}}
   };
 
   // Much more obviously broken: we can't parse the initializer without already
   // knowing whether it produces a noexcept expression.
-  struct TemplateArg {
+  struct TemplateArg { // expected-error {{default member initializer for 'n' needed}}
     int n = // expected-note {{declared here}}
-      ExceptionIf<noexcept(TemplateArg())>::f(); // expected-error {{default member initializer for 'n' needed}}
+      ExceptionIf<noexcept(TemplateArg())>::f(); // expected-note {{in evaluation of exception spec}}
   };
 
   // And within a nested class.
   struct Nested {
-    struct Inner {
+    struct Inner { // expected-error {{default member initializer for 'n' needed}}
       int n = // expected-note {{declared here}}
-        ExceptionIf<noexcept(Nested())>::f();
-    } inner; // expected-error {{default member initializer for 'n' needed}}
+        ExceptionIf<noexcept(Nested())>::f(); // expected-note {{in evaluation of exception spec}}
+    } inner; // expected-note {{in evaluation of exception spec}}
   };
 
   struct Nested2 {
     struct Inner;
-    int n = Inner().n; // expected-error {{initializer for 'n' needed}}
-    struct Inner {
+    int n = Inner().n; // expected-note {{in evaluation of exception spec}}
+    struct Inner { // expected-error {{initializer for 'n' needed}}
       int n = ExceptionIf<noexcept(Nested2())>::f(); // expected-note {{declared here}}
     } inner;
   };
 }
 
 namespace ExceptionSpecification {
-  // FIXME: This diagnostic is quite useless; we should indicate whose
-  // exception specification we were looking for and why.
   struct Nested {
     struct T {
-      T() noexcept(!noexcept(Nested()));
+      T() noexcept(!noexcept(Nested())); // expected-note {{in evaluation of exception spec}}
     } t; // expected-error{{exception specification is not available until end of class definition}}
   };
 }
index 8a13eca2f177170e622a087b7f68cc48d121d8cf..2c4659afa3e74b23cbf1cd82fc94b0aa4a98d4ec 100644 (file)
@@ -13,10 +13,10 @@ public:
 
 bool b();
 int k;
-struct Recurse {
+struct Recurse { // expected-error {{initializer for 'n' needed}}
   int &n = // expected-note {{declared here}}
       b() ?
-      Recurse().n : // expected-error {{initializer for 'n' needed}}
+      Recurse().n : // expected-note {{in evaluation of exception spec}}
       k;
 };
 
@@ -127,19 +127,19 @@ A::A() {}
 namespace template_default_ctor {
 struct A {
   template <typename T>
-  struct B {
+  struct B { // expected-error {{initializer for 'm1' needed}}
     int m1 = 0; // expected-note {{declared here}}
   };
-  enum { NOE = noexcept(B<int>()) }; // expected-error {{initializer for 'm1' needed}}
+  enum { NOE = noexcept(B<int>()) }; // expected-note {{in evaluation of exception spec}}
 };
 }
 
 namespace default_ctor {
 struct A {
-  struct B {
+  struct B { // expected-error {{initializer for 'm1' needed}}
     int m1 = 0; // expected-note {{declared here}}
   };
-  enum { NOE = noexcept(B()) }; // expected-error {{initializer for 'm1' needed}}
+  enum { NOE = noexcept(B()) }; // expected-note {{in evaluation of exception spec}}
 };
 }
 
@@ -147,17 +147,17 @@ namespace member_template {
 struct A {
   template <typename T>
   struct B {
-    struct C {
+    struct C { // expected-error {{initializer for 'm1' needed}}
       int m1 = 0; // expected-note {{declared here}}
     };
     template <typename U>
-    struct D {
+    struct D { // expected-error {{initializer for 'm1' needed}}
       int m1 = 0; // expected-note {{declared here}}
     };
   };
   enum {
-    NOE1 = noexcept(B<int>::C()), // expected-error {{initializer for 'm1' needed}}
-    NOE2 = noexcept(B<int>::D<int>()) // expected-error {{initializer for 'm1' needed}}
+    NOE1 = noexcept(B<int>::C()), // expected-note {{in evaluation of exception spec}}
+    NOE2 = noexcept(B<int>::D<int>()) // expected-note {{in evaluation of exception spec}}
   };
 };
 }
index 51fa6955d0c021435f5d9276bbfeea3a230113b6..b58ad3a15763ef3dd8c5936cb22335eef7bdf191 100644 (file)
@@ -115,8 +115,10 @@ namespace PR13064 {
   struct A { explicit A(int); }; // expected-note{{here}}
   template<typename T> struct B { T a { 0 }; };
   B<A> b;
-  template<typename T> struct C { T a = { 0 }; }; // expected-error{{explicit}}
-  C<A> c; // expected-note {{in instantiation of default member initializer}}
+  template <typename T> struct C { // expected-note {{in instantiation of default member initializer}}
+    T a = {0}; // expected-error{{explicit}}
+  };
+  C<A> c; // expected-note {{in evaluation of exception spec}}
 }
 
 namespace PR16903 {