r284519 - When two function types have equivalent (but distinct) noexcept specifications, create separate type sugar nodes. This is necessary so that substitution into the exception specification will substitute into the correct expression.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 18 12:29:18 PDT 2016


Author: rsmith
Date: Tue Oct 18 14:29:18 2016
New Revision: 284519

URL: http://llvm.org/viewvc/llvm-project?rev=284519&view=rev
Log:
When two function types have equivalent (but distinct) noexcept specifications, create separate type sugar nodes. This is necessary so that substitution into the exception specification will substitute into the correct expression.

Modified:
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/test/SemaCXX/cxx0x-defaulted-functions.cpp
    cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=284519&r1=284518&r2=284519&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Tue Oct 18 14:29:18 2016
@@ -3150,15 +3150,17 @@ static bool isCanonicalExceptionSpecific
     return true;
 
   // A dynamic exception specification is canonical if it only contains pack
-  // expansions (so we can't tell whether it's non-throwing).
+  // expansions (so we can't tell whether it's non-throwing) and all its
+  // contained types are canonical.
   if (ESI.Type == EST_Dynamic) {
     for (QualType ET : ESI.Exceptions)
-      if (!ET->getAs<PackExpansionType>())
+      if (!ET.isCanonical() || !ET->getAs<PackExpansionType>())
         return false;
     return true;
   }
 
-  // A noexcept(expr) specification is canonical if expr is value-dependent.
+  // A noexcept(expr) specification is (possibly) canonical if expr is
+  // value-dependent.
   if (ESI.Type == EST_ComputedNoexcept)
     return ESI.NoexceptExpr && ESI.NoexceptExpr->isValueDependent();
 
@@ -3170,33 +3172,50 @@ ASTContext::getFunctionType(QualType Res
                             const FunctionProtoType::ExtProtoInfo &EPI) const {
   size_t NumArgs = ArgArray.size();
 
-  bool NoexceptInType = getLangOpts().CPlusPlus1z;
+  // Unique functions, to guarantee there is only one function of a particular
+  // structure.
+  llvm::FoldingSetNodeID ID;
+  FunctionProtoType::Profile(ID, ResultTy, ArgArray.begin(), NumArgs, EPI,
+                             *this, true);
+
+  QualType Canonical;
+  bool Unique = false;
 
+  void *InsertPos = nullptr;
+  if (FunctionProtoType *FPT =
+        FunctionProtoTypes.FindNodeOrInsertPos(ID, InsertPos)) {
+    QualType Existing = QualType(FPT, 0);
+
+    // If we find a pre-existing equivalent FunctionProtoType, we can just reuse
+    // it so long as our exception specification doesn't contain a dependent
+    // noexcept expression. If it /does/, we're going to need to create a type
+    // sugar node to hold the concrete expression.
+    if (EPI.ExceptionSpec.Type != EST_ComputedNoexcept ||
+        EPI.ExceptionSpec.NoexceptExpr == FPT->getNoexceptExpr())
+      return Existing;
+
+    // We need a new type sugar node for this one, to hold the new noexcept
+    // expression. We do no canonicalization here, but that's OK since we don't
+    // expect to see the same noexcept expression much more than once.
+    Canonical = getCanonicalType(Existing);
+    Unique = true;
+  }
+
+  bool NoexceptInType = getLangOpts().CPlusPlus1z;
   bool IsCanonicalExceptionSpec =
       isCanonicalExceptionSpecification(EPI.ExceptionSpec, NoexceptInType);
 
   // Determine whether the type being created is already canonical or not.
-  bool isCanonical = IsCanonicalExceptionSpec &&
+  bool isCanonical = !Unique && IsCanonicalExceptionSpec &&
                      isCanonicalResultType(ResultTy) && !EPI.HasTrailingReturn;
   for (unsigned i = 0; i != NumArgs && isCanonical; ++i)
     if (!ArgArray[i].isCanonicalAsParam())
       isCanonical = false;
 
-  // Unique functions, to guarantee there is only one function of a particular
-  // structure.
-  llvm::FoldingSetNodeID ID;
-  FunctionProtoType::Profile(ID, ResultTy, ArgArray.begin(), NumArgs, EPI,
-                             *this, isCanonical);
-
-  void *InsertPos = nullptr;
-  if (FunctionProtoType *FTP =
-        FunctionProtoTypes.FindNodeOrInsertPos(ID, InsertPos))
-    return QualType(FTP, 0);
-
-  // If this type isn't canonical, get the canonical version of it.
-  // The exception spec is not part of the canonical type.
-  QualType Canonical;
-  if (!isCanonical) {
+  // If this type isn't canonical, get the canonical version of it if we don't
+  // already have it. The exception spec is only partially part of the
+  // canonical type, and only in C++17 onwards.
+  if (!isCanonical && Canonical.isNull()) {
     SmallVector<QualType, 16> CanonicalArgs;
     CanonicalArgs.reserve(NumArgs);
     for (unsigned i = 0; i != NumArgs; ++i)
@@ -3208,17 +3227,34 @@ ASTContext::getFunctionType(QualType Res
     if (IsCanonicalExceptionSpec) {
       // Exception spec is already OK.
     } else if (NoexceptInType) {
+      llvm::SmallVector<QualType, 8> ExceptionTypeStorage;
       switch (EPI.ExceptionSpec.Type) {
       case EST_Unparsed: case EST_Unevaluated: case EST_Uninstantiated:
         // We don't know yet. It shouldn't matter what we pick here; no-one
         // should ever look at this.
         LLVM_FALLTHROUGH;
-      case EST_None: case EST_MSAny: case EST_Dynamic:
-        // If we get here for EST_Dynamic, there is at least one
-        // non-pack-expansion type, so this is not non-throwing.
+      case EST_None: case EST_MSAny:
         CanonicalEPI.ExceptionSpec.Type = EST_None;
         break;
 
+        // A dynamic exception specification is almost always "not noexcept",
+        // with the exception that a pack expansion might expand to no types.
+      case EST_Dynamic: {
+        bool AnyPacks = false;
+        for (QualType ET : EPI.ExceptionSpec.Exceptions) {
+          if (ET->getAs<PackExpansionType>())
+            AnyPacks = true;
+          ExceptionTypeStorage.push_back(getCanonicalType(ET));
+        }
+        if (!AnyPacks)
+          CanonicalEPI.ExceptionSpec.Type = EST_None;
+        else {
+          CanonicalEPI.ExceptionSpec.Type = EST_Dynamic;
+          CanonicalEPI.ExceptionSpec.Exceptions = ExceptionTypeStorage;
+        }
+        break;
+      }
+
       case EST_DynamicNone: case EST_BasicNoexcept:
         CanonicalEPI.ExceptionSpec.Type = EST_BasicNoexcept;
         break;
@@ -3289,7 +3325,8 @@ ASTContext::getFunctionType(QualType Res
   FunctionProtoType::ExtProtoInfo newEPI = EPI;
   new (FTP) FunctionProtoType(ResultTy, ArgArray, Canonical, newEPI);
   Types.push_back(FTP);
-  FunctionProtoTypes.InsertNode(FTP, InsertPos);
+  if (!Unique)
+    FunctionProtoTypes.InsertNode(FTP, InsertPos);
   return QualType(FTP, 0);
 }
 

Modified: cfe/trunk/test/SemaCXX/cxx0x-defaulted-functions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx0x-defaulted-functions.cpp?rev=284519&r1=284518&r2=284519&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx0x-defaulted-functions.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx0x-defaulted-functions.cpp Tue Oct 18 14:29:18 2016
@@ -87,17 +87,14 @@ namespace DefaultedFnExceptionSpec {
 
   template<typename T>
   struct Error {
-    // FIXME: Type canonicalization causes all the errors to point at the first
-    // declaration which has the type 'void () noexcept (T::error)'. We should
-    // get one error for 'Error<int>::Error()' and one for 'Error<int>::~Error()'.
-    void f() noexcept(T::error); // expected-error 2{{has no members}}
+    void f() noexcept(T::error);
 
-    Error() noexcept(T::error);
+    Error() noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
     Error(const Error&) noexcept(T::error);
     Error(Error&&) noexcept(T::error);
     Error &operator=(const Error&) noexcept(T::error);
     Error &operator=(Error&&) noexcept(T::error);
-    ~Error() noexcept(T::error);
+    ~Error() noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
   };
 
   struct DelayImplicit {

Modified: cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp?rev=284519&r1=284518&r2=284519&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp Tue Oct 18 14:29:18 2016
@@ -11,3 +11,22 @@ template<bool A, bool B> void redecl2()
 // FIXME: It's not clear whether this is supposed to be valid.
 template<typename A, typename B> void redecl3() throw(A);
 template<typename A, typename B> void redecl3() throw(B);
+
+namespace DependentDefaultCtorExceptionSpec {
+  template<typename> struct T { static const bool value = true; };
+
+  template<class A> struct map {
+    typedef A a;
+    map() noexcept(T<a>::value) {}
+  };
+
+  template<class B> struct multimap {
+    typedef B b;
+    multimap() noexcept(T<b>::value) {}
+  };
+
+  // Don't crash here.
+  struct A { multimap<int> Map; } a;
+
+  static_assert(noexcept(A()));
+}




More information about the cfe-commits mailing list