]> granicus.if.org Git - clang/commitdiff
When two function types have equivalent (but distinct) noexcept specifications, creat...
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 18 Oct 2016 19:29:18 +0000 (19:29 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 18 Oct 2016 19:29:18 +0000 (19:29 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@284519 91177308-0d34-0410-b5e6-96231b3b80d8

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

index 318523eed91df58f52311d3d50a798d47b48ac93..0ac1e174fdc87ef9a9d10fedd7cbdedc20ab143e 100644 (file)
@@ -3150,15 +3150,17 @@ static bool isCanonicalExceptionSpecification(
     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 ResultTy, ArrayRef<QualType> ArgArray,
                             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 ResultTy, ArrayRef<QualType> ArgArray,
     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 ResultTy, ArrayRef<QualType> ArgArray,
   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);
 }
 
index c9c9fcef565bf2f2e666428e6305cda9ec1e77c9..8225b01fd1c3aa975fe571c6200bccc5632c2fb9 100644 (file)
@@ -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 {
index e2a947eac05606fb09ff7ec25458e04fc131f2f4..711e0bca8b164b0f3fd13739de9490190ca40804 100644 (file)
@@ -11,3 +11,22 @@ template<bool A, bool B> void redecl2() noexcept(B); // expected-error {{conflic
 // 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()));
+}