From 5d99c394241eeb23ce3829a3e317d18bad5cc773 Mon Sep 17 00:00:00 2001
From: Richard Smith <richard-llvm@metafoo.co.uk>
Date: Thu, 15 Dec 2016 02:35:39 +0000
Subject: [PATCH] [c++1z] Permit constant evaluation of a call through a
 function pointer whose type differs from the type of the actual function due
 to having a different exception specification.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@289754 91177308-0d34-0410-b5e6-96231b3b80d8
---
 include/clang/AST/ASTContext.h             |  4 +++
 lib/AST/ASTContext.cpp                     |  8 ++++++
 lib/AST/ExprConstant.cpp                   |  5 +++-
 lib/Sema/SemaDecl.cpp                      | 31 +++++++---------------
 test/SemaCXX/constant-expression-cxx1z.cpp |  2 ++
 5 files changed, 28 insertions(+), 22 deletions(-)

diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 4f35508ebac..47d29bc2092 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -1109,6 +1109,10 @@ public:
   /// \brief Change the result type of a function type once it is deduced.
   void adjustDeducedFunctionResultType(FunctionDecl *FD, QualType ResultType);
 
+  /// \brief Determine whether two function types are the same, ignoring
+  /// exception specifications in cases where they're part of the type.
+  bool hasSameFunctionTypeIgnoringExceptionSpec(QualType T, QualType U);
+
   /// \brief Change the exception specification on a function once it is
   /// delay-parsed, instantiated, or computed.
   void adjustExceptionSpec(FunctionDecl *FD,
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 22be71a7272..73ea66e98e8 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -2382,6 +2382,14 @@ static QualType getFunctionTypeWithExceptionSpec(
       Proto->getExtProtoInfo().withExceptionSpec(ESI));
 }
 
+bool ASTContext::hasSameFunctionTypeIgnoringExceptionSpec(QualType T,
+                                                          QualType U) {
+  return hasSameType(T, U) ||
+         (getLangOpts().CPlusPlus1z &&
+          hasSameType(getFunctionTypeWithExceptionSpec(*this, T, EST_None),
+                      getFunctionTypeWithExceptionSpec(*this, U, EST_None)));
+}
+
 void ASTContext::adjustExceptionSpec(
     FunctionDecl *FD, const FunctionProtoType::ExceptionSpecInfo &ESI,
     bool AsWritten) {
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 5dd493cfff5..36f5e6aae55 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -4434,8 +4434,11 @@ public:
       }
 
       // Don't call function pointers which have been cast to some other type.
-      if (!Info.Ctx.hasSameType(CalleeType->getPointeeType(), FD->getType()))
+      // Per DR (no number yet), the caller and callee can differ in noexcept.
+      if (!Info.Ctx.hasSameFunctionTypeIgnoringExceptionSpec(
+              CalleeType->getPointeeType(), FD->getType())) {
         return Error(E);
+      }
     } else
       return Error(E);
 
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 89cb4398638..7878355fc19 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -9076,27 +9076,16 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
       ASTContext::GetBuiltinTypeError Error;
       LookupPredefedObjCSuperType(*this, S, NewFD->getIdentifier());
       QualType T = Context.GetBuiltinType(BuiltinID, Error);
-      if (!T.isNull() && !Context.hasSameType(T, NewFD->getType())) {
-        auto WithoutExceptionSpec = [&](QualType T) -> QualType {
-          auto *Proto = T->getAs<FunctionProtoType>();
-          if (!Proto)
-            return T;
-          return Context.getFunctionType(
-              Proto->getReturnType(), Proto->getParamTypes(),
-              Proto->getExtProtoInfo().withExceptionSpec(EST_None));
-        };
-
-        // If the type of the builtin differs only in its exception
-        // specification, that's OK.
-        // FIXME: If the types do differ in this way, it would be better to
-        // retain the 'noexcept' form of the type.
-        if (!getLangOpts().CPlusPlus1z ||
-            !Context.hasSameType(WithoutExceptionSpec(T),
-                                 WithoutExceptionSpec(NewFD->getType())))
-          // The type of this function differs from the type of the builtin,
-          // so forget about the builtin entirely.
-          Context.BuiltinInfo.forgetBuiltin(BuiltinID, Context.Idents);
-      }
+      // If the type of the builtin differs only in its exception
+      // specification, that's OK.
+      // FIXME: If the types do differ in this way, it would be better to
+      // retain the 'noexcept' form of the type.
+      if (!T.isNull() &&
+          !Context.hasSameFunctionTypeIgnoringExceptionSpec(T,
+                                                            NewFD->getType()))
+        // The type of this function differs from the type of the builtin,
+        // so forget about the builtin entirely.
+        Context.BuiltinInfo.forgetBuiltin(BuiltinID, Context.Idents);
     }
 
     // If this function is declared as being extern "C", then check to see if
diff --git a/test/SemaCXX/constant-expression-cxx1z.cpp b/test/SemaCXX/constant-expression-cxx1z.cpp
index 1563586e196..a48c9b11b88 100644
--- a/test/SemaCXX/constant-expression-cxx1z.cpp
+++ b/test/SemaCXX/constant-expression-cxx1z.cpp
@@ -28,7 +28,9 @@ namespace BaseClassAggregateInit {
 
 namespace NoexceptFunctionTypes {
   template<typename T> constexpr bool f() noexcept(true) { return true; }
+  constexpr bool (*fp)() = f<int>;
   static_assert(f<int>());
+  static_assert(fp());
 
   template<typename T> struct A {
     constexpr bool f() noexcept(true) { return true; }
-- 
GitLab