erichkeane created this revision.
erichkeane added reviewers: rnk, lebedev.ri, aaron.ballman.
Herald added subscribers: cfe-commits, arphaman.
Herald added a project: clang.

In response to https://bugs.llvm.org/show_bug.cgi?id=33235, it became
clear that the current mechanism of hacking through checks for the
exception specification of a function gets confused really quickly when
there are alternate exception specifiers.

      

This patch introduces EST_NoThrow, which is the equivilent of
EST_noexcept when caused by EST_noThrow. The existing implementation is
left in place to cover functions with no FunctionProtoType.


Repository:
  rC Clang

https://reviews.llvm.org/D62435

Files:
  clang/include/clang-c/Index.h
  clang/include/clang/AST/Decl.h
  clang/include/clang/AST/Type.h
  clang/include/clang/Basic/ExceptionSpecificationType.h
  clang/include/clang/Sema/DeclSpec.h
  clang/lib/AST/ASTContext.cpp
  clang/lib/AST/JSONNodeDumper.cpp
  clang/lib/AST/Type.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaType.cpp
  clang/test/SemaCXX/nothrow-vs-exception-specs.cpp
  clang/tools/libclang/CXType.cpp

Index: clang/tools/libclang/CXType.cpp
===================================================================
--- clang/tools/libclang/CXType.cpp
+++ clang/tools/libclang/CXType.cpp
@@ -742,6 +742,8 @@
     return CXCursor_ExceptionSpecificationKind_MSAny;
   case EST_BasicNoexcept:
     return CXCursor_ExceptionSpecificationKind_BasicNoexcept;
+  case EST_NoThrow:
+    return CXCursor_ExceptionSpecificationKind_NoThrow;
   case EST_NoexceptFalse:
   case EST_NoexceptTrue:
   case EST_DependentNoexcept:
Index: clang/test/SemaCXX/nothrow-vs-exception-specs.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/nothrow-vs-exception-specs.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 %s -fcxx-exceptions -fsyntax-only -Wexceptions -verify -std=c++14
+// RUN: %clang_cc1 %s -fcxx-exceptions -fsyntax-only -Wexceptions -verify -std=c++17 -DCPP17
+
+__attribute__((nothrow)) void f1();
+static_assert(noexcept(f1()), "");
+void f1() noexcept;
+// expected-error@+2 {{exception specification in declaration does not match previous declaration}}
+// expected-note@-2 {{previous declaration is here}}
+void f1() noexcept(false);
+
+__attribute__((nothrow)) void f2();
+static_assert(noexcept(f2()), "");
+// expected-error@+2 {{exception specification in declaration does not match previous declaration}}
+// expected-note@-3 {{previous declaration is here}}
+void f2() noexcept(false);
+
+void f3() __attribute__((nothrow));
+static_assert(noexcept(f3()), "");
+void f3() noexcept;
+// expected-error@+2 {{exception specification in declaration does not match previous declaration}}
+// expected-note@-2 {{previous declaration is here}}
+void f3() noexcept(false);
+
+// Still noexcept due to throw()
+__attribute__((nothrow)) void f4() throw();
+static_assert(noexcept(f4()), "");
+
+// Still noexcept due to noexcept
+__attribute__((nothrow)) void f5() noexcept;
+static_assert(noexcept(f5()), "");
+
+// Still noexcept due to noexcept(true)
+__attribute__((nothrow)) void f6() noexcept(true);
+static_assert(noexcept(f6()), "");
+
+#ifndef CPP17
+// Doesn't override C++ implementation.
+__attribute__((nothrow)) void f7() throw(int);
+static_assert(!noexcept(f7()), "");
+#endif
+
+// Doesn't override C++ implementation.
+__attribute__((nothrow)) void f8() noexcept(false);
+static_assert(!noexcept(f8()), "");
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -130,6 +130,7 @@
   case ParsedAttr::AT_Regparm:                                                 \
   case ParsedAttr::AT_AnyX86NoCallerSavedRegisters:                            \
   case ParsedAttr::AT_AnyX86NoCfCheck:                                         \
+  case ParsedAttr::AT_NoThrow:                                                 \
     CALLING_CONV_ATTRS_CASELIST
 
 // Microsoft-specific type qualifiers.
@@ -4516,7 +4517,7 @@
       // If the function declarator has a prototype (i.e. it is not () and
       // does not have a K&R-style identifier list), then the arguments are part
       // of the type, otherwise the argument list is ().
-      const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun;
+      DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun;
       IsQualifiedFunction =
           FTI.hasMethodTypeQualifiers() || FTI.hasRefQualifier();
 
@@ -6945,6 +6946,38 @@
     return true;
   }
 
+  if (attr.getKind() == ParsedAttr::AT_NoThrow) {
+    if (S.CheckAttrNoArgs(attr))
+      return true;
+
+    // Delay if this is not a function type.
+    if (!unwrapped.isFunctionType())
+      return false;
+
+    // Otherwise we can process right away.
+    auto *Proto = unwrapped.get()->getAs<FunctionProtoType>();
+
+    // In the case where this is a FunctionNoProtoType instead of a
+    // FunctionProtoType, let the existing NoThrowAttr implementation do its
+    // thing.
+    if (!Proto)
+      return false;
+
+    attr.setUsedAsTypeAttr();
+
+    // MSVC Ignores nothrow for exception specification if it is in conflict.
+    if (Proto->hasExceptionSpec())
+      return true;
+
+    type = unwrapped.wrap(
+        S, S.Context
+               .getFunctionTypeWithExceptionSpec(
+                   QualType{Proto, 0},
+                   FunctionProtoType::ExceptionSpecInfo{EST_NoThrow})
+               ->getAs<FunctionType>());
+    return true;
+  }
+
   // Delay if the type didn't work out to a function.
   if (!unwrapped.isFunctionType()) return false;
 
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -6064,6 +6064,8 @@
   if (EST2 == EST_BasicNoexcept) return ESI1;
   if (EST1 == EST_NoexceptTrue) return ESI2;
   if (EST2 == EST_NoexceptTrue) return ESI1;
+  if (EST1 == EST_NoThrow) return ESI2;
+  if (EST2 == EST_NoThrow) return ESI1;
 
   // If we're left with value-dependent computed noexcept expressions, we're
   // stuck. Before C++17, we can just drop the exception specification entirely,
@@ -6086,6 +6088,7 @@
   case EST_DependentNoexcept:
   case EST_NoexceptFalse:
   case EST_NoexceptTrue:
+  case EST_NoThrow:
     llvm_unreachable("handled above");
 
   case EST_Dynamic: {
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -192,6 +192,7 @@
   // If this function has a basic noexcept, it doesn't affect the outcome.
   case EST_BasicNoexcept:
   case EST_NoexceptTrue:
+  case EST_NoThrow:
     return;
   // If we're still at noexcept(true) and there's a throw() callee,
   // change to that specification.
@@ -15457,6 +15458,7 @@
   case EST_Uninstantiated:
   case EST_Unevaluated:
   case EST_BasicNoexcept:
+  case EST_NoThrow:
   case EST_DynamicNone:
   case EST_MSAny:
   case EST_None:
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -6853,7 +6853,8 @@
     handleNoCfCheckAttr(S, D, AL);
     break;
   case ParsedAttr::AT_NoThrow:
-    handleSimpleAttribute<NoThrowAttr>(S, D, AL);
+    if (!AL.isUsedAsTypeAttr())
+      handleSimpleAttribute<NoThrowAttr>(S, D, AL);
     break;
   case ParsedAttr::AT_CUDAShared:
     handleSharedAttr(S, D, AL);
Index: clang/lib/AST/Type.cpp
===================================================================
--- clang/lib/AST/Type.cpp
+++ clang/lib/AST/Type.cpp
@@ -3077,6 +3077,7 @@
   case EST_DynamicNone:
   case EST_BasicNoexcept:
   case EST_NoexceptTrue:
+  case EST_NoThrow:
     return CT_Cannot;
 
   case EST_None:
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -464,7 +464,9 @@
     //JOS.attributeWithCall("exceptionSpecExpr",
     //                    [this, E]() { Visit(E.ExceptionSpec.NoexceptExpr); });
     break;
-
+  case EST_NoThrow:
+    JOS.attribute("exceptionSpec", "nothrow");
+    break;
   // FIXME: I cannot find a way to trigger these cases while dumping the AST. I
   // suspect you can only run into them when executing an AST dump from within
   // the debugger, which is not a use case we worry about for the JSON dumping
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -3742,7 +3742,10 @@
         break;
       }
 
-      case EST_DynamicNone: case EST_BasicNoexcept: case EST_NoexceptTrue:
+      case EST_DynamicNone:
+      case EST_BasicNoexcept:
+      case EST_NoexceptTrue:
+      case EST_NoThrow:
         CanonicalEPI.ExceptionSpec.Type = EST_BasicNoexcept;
         break;
 
Index: clang/include/clang/Sema/DeclSpec.h
===================================================================
--- clang/include/clang/Sema/DeclSpec.h
+++ clang/include/clang/Sema/DeclSpec.h
@@ -1544,6 +1544,12 @@
   const ParsedAttributesView &getAttrs() const { return AttrList; }
   ParsedAttributesView &getAttrs() { return AttrList; }
 
+  bool hasAttr(ParsedAttr::Kind Kind) const {
+    return llvm::find_if(getAttrs(), [Kind](const ParsedAttr &P) {
+             return P.getKind() == Kind;
+           }) != getAttrs().end();
+  }
+
   /// Return a DeclaratorChunk for a pointer.
   static DeclaratorChunk getPointer(unsigned TypeQuals, SourceLocation Loc,
                                     SourceLocation ConstQualLoc,
Index: clang/include/clang/Basic/ExceptionSpecificationType.h
===================================================================
--- clang/include/clang/Basic/ExceptionSpecificationType.h
+++ clang/include/clang/Basic/ExceptionSpecificationType.h
@@ -22,6 +22,7 @@
   EST_DynamicNone,      ///< throw()
   EST_Dynamic,          ///< throw(T1, T2)
   EST_MSAny,            ///< Microsoft throw(...) extension
+  EST_NoThrow,          ///< Microsoft declspec(nothrow) extension
   EST_BasicNoexcept,    ///< noexcept
   EST_DependentNoexcept,///< noexcept(expression), value-dependent
   EST_NoexceptFalse,    ///< noexcept(expression), evals to 'false'
@@ -41,7 +42,8 @@
 }
 
 inline bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType) {
-  return ESpecType == EST_BasicNoexcept || isComputedNoexcept(ESpecType);
+  return ESpecType == EST_BasicNoexcept || ESpecType == EST_NoThrow ||
+         isComputedNoexcept(ESpecType);
 }
 
 inline bool isUnresolvedExceptionSpec(ExceptionSpecificationType ESpecType) {
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -3855,6 +3855,7 @@
     case EST_MSAny:
     case EST_BasicNoexcept:
     case EST_Unparsed:
+    case EST_NoThrow:
       return {0, 0, 0};
 
     case EST_Dynamic:
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -2330,6 +2330,14 @@
     return T->castAs<FunctionType>()->getReturnType();
   }
 
+  /// Gets the ExceptionSpecificationType as declared.
+  ExceptionSpecificationType getExceptionSpecType() const {
+    auto *TSI = getTypeSourceInfo();
+    QualType T = TSI ? TSI->getType() : getType();
+    const auto *FPT = T->getAs<FunctionProtoType>();
+    return FPT ? FPT->getExceptionSpecType() : EST_None;
+  }
+
   /// Attempt to compute an informative source range covering the
   /// function exception specification, if any.
   SourceRange getExceptionSpecSourceRange() const;
Index: clang/include/clang-c/Index.h
===================================================================
--- clang/include/clang-c/Index.h
+++ clang/include/clang-c/Index.h
@@ -199,6 +199,11 @@
   CXCursor_ExceptionSpecificationKind_MSAny,
 
   /**
+   * The cursor has a declspec(nothrow) exception specification.
+   */
+  CXCursor_ExceptionSpecificationKind_NoThrow,
+
+  /**
    * The cursor has exception specification basic noexcept.
    */
   CXCursor_ExceptionSpecificationKind_BasicNoexcept,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to