r285150 - Implement name mangling proposal for exception specifications from cxx-abi-dev 2016-10-11.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 25 18:05:54 PDT 2016


Author: rsmith
Date: Tue Oct 25 20:05:54 2016
New Revision: 285150

URL: http://llvm.org/viewvc/llvm-project?rev=285150&view=rev
Log:
Implement name mangling proposal for exception specifications from cxx-abi-dev 2016-10-11.

This has the following ABI impact:

 1) Functions whose parameter or return types are non-throwing function pointer
    types have different manglings in c++1z mode from prior modes. This is
    necessary because c++1z permits overloading on the noexceptness of function
    pointer parameter types. A warning is issued for cases that will change
    manglings in c++1z mode.

 2) Functions whose parameter or return types contain instantiation-dependent
    exception specifications change manglings in all modes. This is necessary
    to support overloading on / SFINAE in these exception specifications, which
    a careful reading of the standard indicates has essentially always been
    permitted.

Note that, in order to be affected by these changes, the code in question must
specify an exception specification on a function pointer/reference type that is
written syntactically within the declaration of another function. Such
declarations are very rare, and I have so far been unable to find any code
that would be affected by this. (Note that such things will probably become
more common in C++17, since it's a lot easier to get a noexcept function type
as a function parameter / return type there.)

This change does not affect the set of symbols produced by a build of clang,
libc++, or libc++abi.

Added:
    cfe/trunk/test/CodeGenCXX/mangle-exception-spec.cpp
Modified:
    cfe/trunk/include/clang/AST/Type.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/AST/ItaniumMangle.cpp
    cfe/trunk/lib/AST/Type.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/TreeTransform.h
    cfe/trunk/test/CXX/drs/dr0xx.cpp
    cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp

Modified: cfe/trunk/include/clang/AST/Type.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=285150&r1=285149&r2=285150&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Type.h (original)
+++ cfe/trunk/include/clang/AST/Type.h Tue Oct 25 20:05:54 2016
@@ -3323,6 +3323,9 @@ public:
   }
   /// Return whether this function has a dependent exception spec.
   bool hasDependentExceptionSpec() const;
+  /// Return whether this function has an instantiation-dependent exception
+  /// spec.
+  bool hasInstantiationDependentExceptionSpec() const;
   /// Result type of getNoexceptSpec().
   enum NoexceptResult {
     NR_NoNoexcept,  ///< There is no noexcept specifier.

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=285150&r1=285149&r2=285150&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Oct 25 20:05:54 2016
@@ -481,6 +481,9 @@ def warn_deprecated_copy_operation : War
   "for %0 is deprecated because it has a user-declared "
   "%select{copy %select{assignment operator|constructor}1|destructor}2">,
   InGroup<Deprecated>, DefaultIgnore;
+def warn_cxx1z_compat_exception_spec_in_signature : Warning<
+  "mangled name of %0 will change in C++17 due to non-throwing exception "
+  "specification in function signature">, InGroup<CXX1zCompat>;
 
 def warn_global_constructor : Warning<
   "declaration requires a global constructor">,

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=285150&r1=285149&r2=285150&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Tue Oct 25 20:05:54 2016
@@ -3153,10 +3153,14 @@ static bool isCanonicalExceptionSpecific
   // 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.isCanonical() || !ET->getAs<PackExpansionType>())
+    bool AnyPackExpansions = false;
+    for (QualType ET : ESI.Exceptions) {
+      if (!ET.isCanonical())
         return false;
-    return true;
+      if (ET->getAs<PackExpansionType>())
+        AnyPackExpansions = true;
+    }
+    return AnyPackExpansions;
   }
 
   // A noexcept(expr) specification is (possibly) canonical if expr is

Modified: cfe/trunk/lib/AST/ItaniumMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ItaniumMangle.cpp?rev=285150&r1=285149&r2=285150&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ItaniumMangle.cpp (original)
+++ cfe/trunk/lib/AST/ItaniumMangle.cpp Tue Oct 25 20:05:54 2016
@@ -2245,6 +2245,22 @@ void CXXNameMangler::mangleType(QualType
   //     they aren't written.
   //   - Conversions on non-type template arguments need to be expressed, since
   //     they can affect the mangling of sizeof/alignof.
+  //
+  // FIXME: This is wrong when mapping to the canonical type for a dependent
+  // type discards instantiation-dependent portions of the type, such as for:
+  //
+  //   template<typename T, int N> void f(T (&)[sizeof(N)]);
+  //   template<typename T> void f(T() throw(typename T::type)); (pre-C++17)
+  //
+  // It's also wrong in the opposite direction when instantiation-dependent,
+  // canonically-equivalent types differ in some irrelevant portion of inner
+  // type sugar. In such cases, we fail to form correct substitutions, eg:
+  //
+  //   template<int N> void f(A<sizeof(N)> *, A<sizeof(N)> (*));
+  //
+  // We should instead canonicalize the non-instantiation-dependent parts,
+  // regardless of whether the type as a whole is dependent or instantiation
+  // dependent.
   if (!T->isInstantiationDependentType() || T->isDependentType())
     T = T.getCanonicalType();
   else {
@@ -2547,6 +2563,24 @@ void CXXNameMangler::mangleType(const Fu
   // e.g. "const" in "int (A::*)() const".
   mangleQualifiers(Qualifiers::fromCVRMask(T->getTypeQuals()));
 
+  // Mangle instantiation-dependent exception-specification, if present,
+  // per cxx-abi-dev proposal on 2016-10-11.
+  if (T->hasInstantiationDependentExceptionSpec()) {
+    if (T->getExceptionSpecType() == EST_ComputedNoexcept) {
+      Out << "nX";
+      mangleExpression(T->getNoexceptExpr());
+      Out << "E";
+    } else {
+      assert(T->getExceptionSpecType() == EST_Dynamic);
+      Out << "tw";
+      for (auto ExceptTy : T->exceptions())
+        mangleType(ExceptTy);
+      Out << "E";
+    }
+  } else if (T->isNothrow(getASTContext())) {
+    Out << "nx";
+  }
+
   Out << 'F';
 
   // FIXME: We don't have enough information in the AST to produce the 'Y'

Modified: cfe/trunk/lib/AST/Type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Type.cpp?rev=285150&r1=285149&r2=285150&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Type.cpp (original)
+++ cfe/trunk/lib/AST/Type.cpp Tue Oct 25 20:05:54 2016
@@ -2791,6 +2791,15 @@ bool FunctionProtoType::hasDependentExce
   return false;
 }
 
+bool FunctionProtoType::hasInstantiationDependentExceptionSpec() const {
+  if (Expr *NE = getNoexceptExpr())
+    return NE->isInstantiationDependent();
+  for (QualType ET : exceptions())
+    if (ET->isInstantiationDependentType())
+      return true;
+  return false;
+}
+
 FunctionProtoType::NoexceptResult
 FunctionProtoType::getNoexceptSpec(const ASTContext &ctx) const {
   ExceptionSpecificationType est = getExceptionSpecType();

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=285150&r1=285149&r2=285150&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue Oct 25 20:05:54 2016
@@ -8996,6 +8996,42 @@ bool Sema::CheckFunctionDeclaration(Scop
                !R->isObjCObjectPointerType())
         Diag(NewFD->getLocation(), diag::warn_return_value_udt) << NewFD << R;
     }
+
+    // C++1z [dcl.fct]p6:
+    //   [...] whether the function has a non-throwing exception-specification
+    //   [is] part of the function type
+    //
+    // This results in an ABI break between C++14 and C++17 for functions whose
+    // declared type includes an exception-specification in a parameter or
+    // return type. (Exception specifications on the function itself are OK in
+    // most cases, and exception specifications are not permitted in most other
+    // contexts where they could make it into a mangling.)
+    if (!getLangOpts().CPlusPlus1z && !NewFD->getPrimaryTemplate()) {
+      auto HasNoexcept = [&](QualType T) -> bool {
+        // Strip off declarator chunks that could be between us and a function
+        // type. We don't need to look far, exception specifications are very
+        // restricted prior to C++17.
+        if (auto *RT = T->getAs<ReferenceType>())
+          T = RT->getPointeeType();
+        else if (T->isAnyPointerType())
+          T = T->getPointeeType();
+        else if (auto *MPT = T->getAs<MemberPointerType>())
+          T = MPT->getPointeeType();
+        if (auto *FPT = T->getAs<FunctionProtoType>())
+          if (FPT->isNothrow(Context))
+            return true;
+        return false;
+      };
+
+      auto *FPT = NewFD->getType()->castAs<FunctionProtoType>();
+      bool AnyNoexcept = HasNoexcept(FPT->getReturnType());
+      for (QualType T : FPT->param_types())
+        AnyNoexcept |= HasNoexcept(T);
+      if (AnyNoexcept)
+        Diag(NewFD->getLocation(),
+             diag::warn_cxx1z_compat_exception_spec_in_signature)
+            << NewFD;
+    }
   }
   return Redeclaration;
 }

Modified: cfe/trunk/lib/Sema/TreeTransform.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=285150&r1=285149&r2=285150&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/TreeTransform.h (original)
+++ cfe/trunk/lib/Sema/TreeTransform.h Tue Oct 25 20:05:54 2016
@@ -5119,6 +5119,8 @@ bool TreeTransform<Derived>::TransformEx
   }
 
   ESI.Exceptions = Exceptions;
+  if (ESI.Exceptions.empty())
+    ESI.Type = EST_DynamicNone;
   return false;
 }
 

Modified: cfe/trunk/test/CXX/drs/dr0xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr0xx.cpp?rev=285150&r1=285149&r2=285150&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr0xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr0xx.cpp Tue Oct 25 20:05:54 2016
@@ -1030,7 +1030,7 @@ namespace dr92 { // dr92: 4.0 c++17
   // expected-error at -4 {{cannot initialize}}
 #endif
 
-  void g(void() throw()); // expected-note 0-2 {{no known conversion}}
+  void g(void() throw()); // expected-note 0-2 {{no known conversion}} expected-warning 0-1{{mangled name of 'g' will change in C++17}}
   void h() throw() {
     g(f); // expected-error-re {{{{is not superset|no matching function}}}}
     g(q); // expected-error-re {{{{is not superset|no matching function}}}}

Added: cfe/trunk/test/CodeGenCXX/mangle-exception-spec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/mangle-exception-spec.cpp?rev=285150&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/mangle-exception-spec.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/mangle-exception-spec.cpp Tue Oct 25 20:05:54 2016
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 | FileCheck %s --check-prefix CHECK --check-prefix CHECK-CXX11
+// RUN: %clang_cc1 -std=c++1z -emit-llvm %s -o - -triple=x86_64-apple-darwin9 | FileCheck %s --check-prefix CHECK --check-prefix CHECK-CXX17
+
+// CHECK: define {{.*}} @_Z1aPFivE(
+void a(int() throw(int, float)) {}
+// CHECK-CXX11: define {{.*}} @_Z1bPFivE(
+// CHECK-CXX17: define {{.*}} @_Z1bPnxFivE(
+void b(int() noexcept) {}
+// CHECK-CXX11: define {{.*}} @_Z1cPFivE(
+// CHECK-CXX17: define {{.*}} @_Z1cPnxFivE(
+void c(int() throw()) {}
+// CHECK: define {{.*}} @_Z1dPFivE(
+void d(int() noexcept(false)) {}
+// CHECK-CXX11: define {{.*}} @_Z1ePFivE(
+// CHECK-CXX17: define {{.*}} @_Z1ePnxFivE(
+void e(int() noexcept(true)) {}
+
+template<bool B> void f(int() noexcept(B)) {}
+// CHECK: define {{.*}} @_Z1fILb0EEvPnXT_EFivE(
+template void f<false>(int());
+// CHECK: define {{.*}} @_Z1fILb1EEvPnXT_EFivE(
+template void f<true>(int() noexcept);
+
+template<typename...T> void g(int() throw(T...)) {}
+// CHECK: define {{.*}} @_Z1gIJEEvPtwDpT_EFivE(
+template void g<>(int() noexcept);
+// CHECK: define {{.*}} @_Z1gIJfEEvPtwDpT_EFivE(
+template void g<float>(int());
+
+// We consider the exception specifications in parameter and return type here
+// to be different.
+template<typename...T> auto h(int() throw(int, T...)) -> int (*)() throw(T..., int) { return nullptr; }
+// CHECK: define {{.*}} @_Z1hIJEEPtwDpT_iEFivEPtwiS1_EFivE(
+template auto h<>(int()) -> int (*)();
+// CHECK: define {{.*}} @_Z1hIJfEEPtwDpT_iEFivEPtwiS1_EFivE(
+template auto h<float>(int()) -> int (*)();
+
+// FIXME: The C++11 manglings here are wrong; they should be the same as the
+// C++17 manglings.
+// The mangler mishandles substitutions for instantiation-dependent types that
+// differ only in type sugar that is not relevant for mangling. (In this case,
+// the types differ in presence/absence of ParenType nodes under the pointer.)
+template<typename...T> auto i(int() throw(int, T...)) -> int (*)() throw(int, T...) { return nullptr; }
+// CHECK-CXX11: define {{.*}} @_Z1iIJEEPtwiDpT_EFivEPS2_(
+// CHECK-CXX17: define {{.*}} @_Z1iIJEEPtwiDpT_EFivES3_(
+template auto i<>(int()) -> int (*)();
+// CHECK-CXX11: define {{.*}} @_Z1iIJfEEPtwiDpT_EFivEPS2_(
+// CHECK-CXX17: define {{.*}} @_Z1iIJfEEPtwiDpT_EFivES3_(
+template auto i<float>(int()) -> int (*)();

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=285150&r1=285149&r2=285150&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1z-noexcept-function-type.cpp Tue Oct 25 20:05:54 2016
@@ -1,5 +1,8 @@
+// RUN: %clang_cc1 -std=c++14 -verify %s
 // RUN: %clang_cc1 -std=c++1z -verify %s
 
+#if __cplusplus > 201402L
+
 template<typename T> void redecl1() noexcept(noexcept(T())) {} // expected-note {{previous}}
 template<typename T> void redecl1() noexcept(noexcept(T())); // ok, same type
 template<typename T> void redecl1() noexcept(noexcept(T())) {} // expected-error {{redefinition}}
@@ -34,3 +37,43 @@ namespace DependentDefaultCtorExceptionS
 
   static_assert(noexcept(A()));
 }
+
+#endif
+
+namespace CompatWarning {
+  struct X;
+
+  // These cases don't change.
+  void f0(void p() throw(int));
+  auto f0() -> void (*)() noexcept(false);
+
+  // These cases take an ABI break in C++17 because their parameter / return types change.
+  void f1(void p() noexcept);
+  void f2(void (*p)() noexcept(true));
+  void f3(void (&p)() throw());
+  void f4(void (X::*p)() throw());
+  auto f5() -> void (*)() throw();
+  auto f6() -> void (&)() throw();
+  auto f7() -> void (X::*)() throw();
+#if __cplusplus <= 201402L
+  // expected-warning at -8 {{mangled name of 'f1' will change in C++17 due to non-throwing exception specification in function signature}}
+  // expected-warning at -8 {{mangled name of 'f2' will change in C++17 due to non-throwing exception specification in function signature}}
+  // expected-warning at -8 {{mangled name of 'f3' will change in C++17 due to non-throwing exception specification in function signature}}
+  // expected-warning at -8 {{mangled name of 'f4' will change in C++17 due to non-throwing exception specification in function signature}}
+  // expected-warning at -8 {{mangled name of 'f5' will change in C++17 due to non-throwing exception specification in function signature}}
+  // expected-warning at -8 {{mangled name of 'f6' will change in C++17 due to non-throwing exception specification in function signature}}
+  // expected-warning at -8 {{mangled name of 'f7' will change in C++17 due to non-throwing exception specification in function signature}}
+#endif
+
+  // An instantiation-dependent exception specification needs to be mangled in
+  // all language modes, since it participates in SFINAE.
+  template<typename T> void g(void() throw(T)); // expected-note {{substitution failure}}
+  template<typename T> void g(...) = delete; // expected-note {{deleted}}
+  void test_g() { g<void>(nullptr); } // expected-error {{deleted}}
+
+  // An instantiation-dependent exception specification needs to be mangled in
+  // all language modes, since it participates in SFINAE.
+  template<typename T> void h(void() noexcept(T())); // expected-note {{substitution failure}}
+  template<typename T> void h(...) = delete; // expected-note {{deleted}}
+  void test_h() { h<void>(nullptr); } // expected-error {{deleted}}
+}




More information about the cfe-commits mailing list