https://github.com/ahatanak updated 
https://github.com/llvm/llvm-project/pull/110048

>From b9a8339220635b9a0b2d77a11d80b496a18a917a Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahata...@gmail.com>
Date: Wed, 25 Sep 2024 14:47:05 -0700
Subject: [PATCH] [NFC] Add implicit cast kinds for function pointer
 conversions

The new cast kinds are needed to distinguish between no-op conversions
and conversions from pointers to noexcept functions to pointers to
functions without noexcept as the latter can cause function pointers to
be re-signed on arm64e.

See https://github.com/llvm/llvm-project/pull/109056 for background.
---
 .../bugprone/SwappedArgumentsCheck.cpp        |  2 ++
 .../ProTypeCstyleCastCheck.cpp                |  4 ++-
 .../google/AvoidCStyleCastsCheck.cpp          |  7 ++++-
 .../clang-tidy/modernize/LoopConvertCheck.cpp |  4 ++-
 .../ImplicitConversionInLoopCheck.cpp         |  4 ++-
 .../MakeMemberFunctionConstCheck.cpp          |  9 ++++--
 .../clang-tidy/utils/DeclRefExprUtils.cpp     |  2 ++
 clang-tools-extra/clangd/Hover.cpp            |  2 ++
 clang/include/clang/AST/IgnoreExpr.h          |  4 ++-
 clang/include/clang/AST/OperationKinds.def    |  8 +++++
 .../lib/ARCMigrate/TransBlockObjCVariable.cpp |  6 ++--
 clang/lib/AST/ByteCode/Compiler.cpp           |  4 +++
 clang/lib/AST/Expr.cpp                        | 28 +++++++++++++----
 clang/lib/AST/ExprCXX.cpp                     |  6 +++-
 clang/lib/AST/ExprConstant.cpp                | 17 +++++++++--
 clang/lib/Analysis/CFG.cpp                    |  2 ++
 clang/lib/Analysis/ExprMutationAnalyzer.cpp   | 12 ++++----
 clang/lib/Analysis/FlowSensitive/Transfer.cpp |  8 +++--
 clang/lib/Analysis/ThreadSafety.cpp           |  4 ++-
 clang/lib/Analysis/ThreadSafetyCommon.cpp     |  2 ++
 clang/lib/CodeGen/CGDecl.cpp                  |  2 ++
 clang/lib/CodeGen/CGExpr.cpp                  |  6 +++-
 clang/lib/CodeGen/CGExprAgg.cpp               |  4 +++
 clang/lib/CodeGen/CGExprComplex.cpp           |  2 ++
 clang/lib/CodeGen/CGExprConstant.cpp          |  2 ++
 clang/lib/CodeGen/CGExprScalar.cpp            |  4 ++-
 clang/lib/Edit/RewriteObjCFoundationAPI.cpp   |  2 ++
 clang/lib/Sema/CheckExprLifetime.cpp          |  2 ++
 clang/lib/Sema/SemaChecking.cpp               |  4 +++
 clang/lib/Sema/SemaDecl.cpp                   |  4 ++-
 clang/lib/Sema/SemaDeclCXX.cpp                |  4 ++-
 clang/lib/Sema/SemaExpr.cpp                   |  2 ++
 clang/lib/Sema/SemaExprCXX.cpp                |  6 +++-
 clang/lib/Sema/SemaInit.cpp                   |  4 +--
 clang/lib/Sema/SemaOverload.cpp               |  6 +++-
 clang/lib/Sema/SemaStmt.cpp                   |  2 ++
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp |  2 ++
 clang/lib/StaticAnalyzer/Core/SValBuilder.cpp |  2 ++
 .../ast-dump-function-pointer-conversion.cpp  | 30 +++++++++++++++++++
 .../dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp |  4 +--
 40 files changed, 193 insertions(+), 36 deletions(-)
 create mode 100644 clang/test/AST/ast-dump-function-pointer-conversion.cpp

diff --git a/clang-tools-extra/clang-tidy/bugprone/SwappedArgumentsCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/SwappedArgumentsCheck.cpp
index 8989444dde1300..acb7112fa2abed 100644
--- a/clang-tools-extra/clang-tidy/bugprone/SwappedArgumentsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/SwappedArgumentsCheck.cpp
@@ -27,6 +27,8 @@ void SwappedArgumentsCheck::registerMatchers(MatchFinder 
*Finder) {
 static const Expr *ignoreNoOpCasts(const Expr *E) {
   if (auto *Cast = dyn_cast<CastExpr>(E))
     if (Cast->getCastKind() == CK_LValueToRValue ||
+        Cast->getCastKind() == CK_FunctionPointerConversion ||
+        Cast->getCastKind() == CK_MemberFunctionPointerConversion ||
         Cast->getCastKind() == CK_NoOp)
       return ignoreNoOpCasts(Cast->getSubExpr());
   return E;
diff --git 
a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp 
b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp
index 5e255dcaacd262..bab08496e90e86 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp
@@ -90,7 +90,9 @@ void ProTypeCstyleCastCheck::check(const 
MatchFinder::MatchResult &Result) {
     return;
   }
 
-  if (MatchedCast->getCastKind() == CK_NoOp &&
+  if ((MatchedCast->getCastKind() == CK_NoOp ||
+       MatchedCast->getCastKind() == CK_FunctionPointerConversion ||
+       MatchedCast->getCastKind() == CK_MemberFunctionPointerConversion) &&
       needsConstCast(SourceType, MatchedCast->getType())) {
     diag(MatchedCast->getBeginLoc(),
          "do not use C-style cast to cast away constness");
diff --git a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp 
b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
index 3109bbb3724c79..888f7b122b82c3 100644
--- a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
@@ -123,7 +123,10 @@ void AvoidCStyleCastsCheck::check(const 
MatchFinder::MatchResult &Result) {
       DestTypeAsWritten->isRecordType() &&
       !DestTypeAsWritten->isElaboratedTypeSpecifier();
 
-  if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
+  if ((CastExpr->getCastKind() == CK_NoOp ||
+       CastExpr->getCastKind() == CK_FunctionPointerConversion ||
+       CastExpr->getCastKind() == CK_MemberFunctionPointerConversion) &&
+      !FnToFnCast) {
     // Function pointer/reference casts may be needed to resolve ambiguities in
     // case of overloaded functions, so detection of redundant casts is 
trickier
     // in this case. Don't emit "redundant cast" warnings for function
@@ -201,6 +204,8 @@ void AvoidCStyleCastsCheck::check(const 
MatchFinder::MatchResult &Result) {
     }
     return;
   case CK_NoOp:
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion:
     if (FnToFnCast) {
       ReplaceWithNamedCast("static_cast");
       return;
diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp 
b/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp
index 1c6a1618ebbc4f..801d97ef2988ba 100644
--- a/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp
@@ -500,7 +500,9 @@ static bool canBeModified(ASTContext *Context, const Expr 
*E) {
   if (Parents.size() != 1)
     return true;
   if (const auto *Cast = Parents[0].get<ImplicitCastExpr>()) {
-    if ((Cast->getCastKind() == CK_NoOp &&
+    if (((Cast->getCastKind() == CK_NoOp ||
+          Cast->getCastKind() == CK_FunctionPointerConversion ||
+          Cast->getCastKind() == CK_MemberFunctionPointerConversion) &&
          Context->hasSameType(Cast->getType(), E->getType().withConst())) ||
         (Cast->getCastKind() == CK_LValueToRValue &&
          !Cast->getType().isNull() && Cast->getType()->isFundamentalType()))
diff --git 
a/clang-tools-extra/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp 
b/clang-tools-extra/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp
index 86fca0722dcd82..4fc32e1d1572eb 100644
--- a/clang-tools-extra/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp
+++ b/clang-tools-extra/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp
@@ -24,7 +24,9 @@ namespace clang::tidy::performance {
 // case we skip the first cast expr.
 static bool isNonTrivialImplicitCast(const Stmt *ST) {
   if (const auto *ICE = dyn_cast<ImplicitCastExpr>(ST)) {
-    return (ICE->getCastKind() != CK_NoOp) ||
+    return (ICE->getCastKind() != CK_NoOp &&
+            ICE->getCastKind() != CK_FunctionPointerConversion &&
+            ICE->getCastKind() != CK_MemberFunctionPointerConversion) ||
            isNonTrivialImplicitCast(ICE->getSubExpr());
   }
   return false;
diff --git 
a/clang-tools-extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp 
b/clang-tools-extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp
index d42fcba70e81b4..751cd637a02334 100644
--- a/clang-tools-extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp
@@ -97,7 +97,9 @@ class FindUsageOfThis : public 
RecursiveASTVisitor<FindUsageOfThis> {
   //  (possibly `-UnaryOperator Deref)
   //        `-CXXThisExpr 'S *' this
   bool visitUser(const ImplicitCastExpr *Cast) {
-    if (Cast->getCastKind() != CK_NoOp)
+    if (Cast->getCastKind() != CK_NoOp &&
+        Cast->getCastKind() != CK_FunctionPointerConversion &&
+        Cast->getCastKind() != CK_MemberFunctionPointerConversion)
       return false; // Stop traversal.
 
     // Only allow NoOp cast to 'const S' or 'const S *'.
@@ -159,7 +161,10 @@ class FindUsageOfThis : public 
RecursiveASTVisitor<FindUsageOfThis> {
       if (Cast->getCastKind() == CK_LValueToRValue)
         return true;
 
-      if (Cast->getCastKind() == CK_NoOp && Cast->getType().isConstQualified())
+      if ((Cast->getCastKind() == CK_NoOp ||
+           Cast->getCastKind() == CK_FunctionPointerConversion ||
+           Cast->getCastKind() == CK_MemberFunctionPointerConversion) &&
+          Cast->getType().isConstQualified())
         return true;
     }
 
diff --git a/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp 
b/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
index 106feb7fb41720..87e523eaaa7718 100644
--- a/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
+++ b/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp
@@ -240,6 +240,8 @@ AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, 
Indirections) {
         case CK_BaseToDerived:
         case CK_DerivedToBase:
         case CK_UncheckedDerivedToBase:
+        case CK_FunctionPointerConversion:
+        case CK_MemberFunctionPointerConversion:
         case CK_Dynamic:
         case CK_BaseToDerivedMemberPointer:
         case CK_DerivedToBaseMemberPointer:
diff --git a/clang-tools-extra/clangd/Hover.cpp 
b/clang-tools-extra/clangd/Hover.cpp
index 298fa79e3fd0ba..5cb2ecd1d55726 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -1118,6 +1118,8 @@ void maybeAddCalleeArgInfo(const SelectionTree::Node *N, 
HoverInfo &HI,
     if (const auto *ImplicitCast = CastNode->ASTNode.get<ImplicitCastExpr>()) {
       switch (ImplicitCast->getCastKind()) {
       case CK_NoOp:
+      case CK_FunctionPointerConversion:
+      case CK_MemberFunctionPointerConversion:
       case CK_DerivedToBase:
       case CK_UncheckedDerivedToBase:
         // If it was a reference before, it's still a reference.
diff --git a/clang/include/clang/AST/IgnoreExpr.h 
b/clang/include/clang/AST/IgnoreExpr.h
index 917bada61fa6fd..967c381dfa9dc5 100644
--- a/clang/include/clang/AST/IgnoreExpr.h
+++ b/clang/include/clang/AST/IgnoreExpr.h
@@ -102,7 +102,9 @@ inline Expr *IgnoreBaseCastsSingleStep(Expr *E) {
   if (auto *CE = dyn_cast<CastExpr>(E))
     if (CE->getCastKind() == CK_DerivedToBase ||
         CE->getCastKind() == CK_UncheckedDerivedToBase ||
-        CE->getCastKind() == CK_NoOp)
+        CE->getCastKind() == CK_NoOp ||
+        CE->getCastKind() == CK_FunctionPointerConversion ||
+        CE->getCastKind() == CK_MemberFunctionPointerConversion)
       return CE->getSubExpr();
 
   return E;
diff --git a/clang/include/clang/AST/OperationKinds.def 
b/clang/include/clang/AST/OperationKinds.def
index 8788b8ff0ef0a4..25cd0dbb5e3a3b 100644
--- a/clang/include/clang/AST/OperationKinds.def
+++ b/clang/include/clang/AST/OperationKinds.def
@@ -84,6 +84,14 @@ CAST_OPERATION(LValueToRValue)
 ///   void () noexcept -> void ()
 CAST_OPERATION(NoOp)
 
+/// CK_FunctionPointerConversion - A conversion from pointer/reference to
+/// noexcept function to pointer/reference to function.
+CAST_OPERATION(FunctionPointerConversion)
+
+/// CK_MemberFunctionPointerConversion - A conversion from pointer to noexcept
+/// member function to pointer to member function.
+CAST_OPERATION(MemberFunctionPointerConversion)
+
 /// CK_BaseToDerived - A conversion from a C++ class pointer/reference
 /// to a derived class pointer/reference.
 ///   B *b = static_cast<B*>(a);
diff --git a/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp 
b/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp
index 1e4db33135b6a1..7295f064849ea6 100644
--- a/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp
+++ b/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp
@@ -53,8 +53,10 @@ class RootBlockObjCVarRewriter :
         if (ref->getDecl() == Var) {
           if (castE->getCastKind() == CK_LValueToRValue)
             return true; // Using the value of the variable.
-          if (castE->getCastKind() == CK_NoOp && castE->isLValue() &&
-              Var->getASTContext().getLangOpts().CPlusPlus)
+          if ((castE->getCastKind() == CK_NoOp ||
+               castE->getCastKind() == CK_FunctionPointerConversion ||
+               castE->getCastKind() == CK_MemberFunctionPointerConversion) &&
+              castE->isLValue() && 
Var->getASTContext().getLangOpts().CPlusPlus)
             return true; // Binding to const C++ reference.
         }
       }
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index e54b6568d7060b..7d448d0e1cbeb2 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -429,6 +429,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
   case CK_FunctionToPointerDecay:
   case CK_NonAtomicToAtomic:
   case CK_NoOp:
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion:
   case CK_UserDefinedConversion:
   case CK_AddressSpaceConversion:
   case CK_CPointerToObjCPointerCast:
@@ -3161,6 +3163,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr 
*E) {
     for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped);
          Stripped = ICE->getSubExpr())
       if (ICE->getCastKind() != CK_NoOp &&
+          ICE->getCastKind() != CK_FunctionPointerConversion &&
+          ICE->getCastKind() != CK_MemberFunctionPointerConversion &&
           ICE->getCastKind() != CK_IntegralCast)
         break;
 
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 2e463fc00c6b68..c62ffa9bf7df59 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -98,7 +98,9 @@ const Expr *Expr::skipRValueSubobjectAdjustments(
         continue;
       }
 
-      if (CE->getCastKind() == CK_NoOp) {
+      if (CE->getCastKind() == CK_NoOp ||
+          CE->getCastKind() == CK_FunctionPointerConversion ||
+          CE->getCastKind() == CK_MemberFunctionPointerConversion) {
         E = CE->getSubExpr();
         continue;
       }
@@ -1926,6 +1928,8 @@ bool CastExpr::CastConsistency() const {
   case CK_Dependent:
   case CK_LValueToRValue:
   case CK_NoOp:
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion:
   case CK_AtomicToNonAtomic:
   case CK_NonAtomicToAtomic:
   case CK_PointerToBoolean:
@@ -3188,7 +3192,9 @@ static const Expr 
*skipTemporaryBindingsNoOpCastsAndParens(const Expr *E) {
     E = M->getSubExpr();
 
   while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
-    if (ICE->getCastKind() == CK_NoOp)
+    if (ICE->getCastKind() == CK_NoOp ||
+        ICE->getCastKind() == CK_FunctionPointerConversion ||
+        ICE->getCastKind() == CK_MemberFunctionPointerConversion)
       E = ICE->getSubExpr();
     else
       break;
@@ -3198,7 +3204,9 @@ static const Expr 
*skipTemporaryBindingsNoOpCastsAndParens(const Expr *E) {
     E = BE->getSubExpr();
 
   while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
-    if (ICE->getCastKind() == CK_NoOp)
+    if (ICE->getCastKind() == CK_NoOp ||
+        ICE->getCastKind() == CK_FunctionPointerConversion ||
+        ICE->getCastKind() == CK_MemberFunctionPointerConversion)
       E = ICE->getSubExpr();
     else
       break;
@@ -3263,6 +3271,8 @@ bool Expr::isImplicitCXXThis() const {
 
     if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
       if (ICE->getCastKind() == CK_NoOp ||
+          ICE->getCastKind() == CK_FunctionPointerConversion ||
+          ICE->getCastKind() == CK_MemberFunctionPointerConversion ||
           ICE->getCastKind() == CK_LValueToRValue ||
           ICE->getCastKind() == CK_DerivedToBase ||
           ICE->getCastKind() == CK_UncheckedDerivedToBase) {
@@ -3478,6 +3488,8 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool 
IsForRef,
 
     // Handle misc casts we want to ignore.
     if (CE->getCastKind() == CK_NoOp ||
+        CE->getCastKind() == CK_FunctionPointerConversion ||
+        CE->getCastKind() == CK_MemberFunctionPointerConversion ||
         CE->getCastKind() == CK_LValueToRValue ||
         CE->getCastKind() == CK_ToUnion ||
         CE->getCastKind() == CK_ConstructorConversion ||
@@ -4113,7 +4125,10 @@ FieldDecl *Expr::getSourceBitField() {
 
   while (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
     if (ICE->getCastKind() == CK_LValueToRValue ||
-        (ICE->isGLValue() && ICE->getCastKind() == CK_NoOp))
+        (ICE->isGLValue() &&
+         (ICE->getCastKind() == CK_NoOp ||
+          ICE->getCastKind() == CK_FunctionPointerConversion ||
+          ICE->getCastKind() == CK_MemberFunctionPointerConversion)))
       E = ICE->getSubExpr()->IgnoreParens();
     else
       break;
@@ -4167,7 +4182,10 @@ bool Expr::refersToVectorElement() const {
   const Expr *E = this->IgnoreParens();
 
   while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
-    if (ICE->isGLValue() && ICE->getCastKind() == CK_NoOp)
+    if (ICE->isGLValue() &&
+        (ICE->getCastKind() == CK_NoOp ||
+         ICE->getCastKind() == CK_FunctionPointerConversion ||
+         ICE->getCastKind() == CK_MemberFunctionPointerConversion))
       E = ICE->getSubExpr()->IgnoreParens();
     else
       break;
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 83ce404add5f50..a3f09786960b1f 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -344,8 +344,12 @@ QualType CXXDeleteExpr::getDestroyedType() const {
   while (const auto *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
     if (ICE->getCastKind() == CK_DerivedToBase ||
         ICE->getCastKind() == CK_UncheckedDerivedToBase ||
-        ICE->getCastKind() == CK_NoOp) {
+        ICE->getCastKind() == CK_NoOp ||
+        ICE->getCastKind() == CK_FunctionPointerConversion ||
+        ICE->getCastKind() == CK_MemberFunctionPointerConversion) {
       assert((ICE->getCastKind() == CK_NoOp ||
+              ICE->getCastKind() == CK_FunctionPointerConversion ||
+              ICE->getCastKind() == CK_MemberFunctionPointerConversion ||
               getOperatorDelete()->isDestroyingOperatorDelete()) &&
              "only a destroying operator delete can have a converted arg");
       Arg = ICE->getSubExpr();
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 6387e375dda79c..28f829e612be47 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -6247,7 +6247,9 @@ static bool MaybeHandleUnionActiveMemberChange(EvalInfo 
&Info,
     } else if (auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
       // Step over a derived-to-base conversion.
       E = ICE->getSubExpr();
-      if (ICE->getCastKind() == CK_NoOp)
+      if (ICE->getCastKind() == CK_NoOp ||
+          ICE->getCastKind() == CK_FunctionPointerConversion ||
+          ICE->getCastKind() == CK_MemberFunctionPointerConversion)
         continue;
       if (ICE->getCastKind() != CK_DerivedToBase &&
           ICE->getCastKind() != CK_UncheckedDerivedToBase)
@@ -8301,6 +8303,8 @@ class ExprEvaluatorBase
     }
 
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
     case CK_UserDefinedConversion:
       return StmtVisitorTy::Visit(E->getSubExpr());
 
@@ -10074,6 +10078,8 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const 
CXXNewExpr *E) {
     for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped);
          Stripped = ICE->getSubExpr())
       if (ICE->getCastKind() != CK_NoOp &&
+          ICE->getCastKind() != CK_FunctionPointerConversion &&
+          ICE->getCastKind() != CK_MemberFunctionPointerConversion &&
           ICE->getCastKind() != CK_IntegralCast)
         break;
 
@@ -12210,8 +12216,9 @@ static const Expr *ignorePointerCastsAndParens(const 
Expr *E) {
   // We only conservatively allow a few kinds of casts, because this code is
   // inherently a simple solution that seeks to support the common case.
   auto CastKind = Cast->getCastKind();
-  if (CastKind != CK_NoOp && CastKind != CK_BitCast &&
-      CastKind != CK_AddressSpaceConversion)
+  if (CastKind != CK_NoOp && CastKind != CK_FunctionPointerConversion &&
+      CastKind != CK_MemberFunctionPointerConversion &&
+      CastKind != CK_BitCast && CastKind != CK_AddressSpaceConversion)
     return NoParens;
 
   const auto *SubExpr = Cast->getSubExpr();
@@ -14448,6 +14455,8 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) 
{
   QualType SrcType = SubExpr->getType();
 
   switch (E->getCastKind()) {
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion:
   case CK_BaseToDerived:
   case CK_DerivedToBase:
   case CK_UncheckedDerivedToBase:
@@ -15288,6 +15297,8 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr 
*E) {
   case CK_BaseToDerived:
   case CK_DerivedToBase:
   case CK_UncheckedDerivedToBase:
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion:
   case CK_Dynamic:
   case CK_ToUnion:
   case CK_ArrayToPointerDecay:
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index f678ac6f2ff36a..baa5f96f8cbdbe 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -1491,6 +1491,8 @@ void CFGBuilder::findConstructionContexts(
     // Should we support other implicit cast kinds?
     switch (Cast->getCastKind()) {
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
     case CK_ConstructorConversion:
       findConstructionContexts(Layer, Cast->getSubExpr());
       break;
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp 
b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index 6d726ae44104ed..8f19bd7e918706 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -377,11 +377,13 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const 
Expr *Exp) {
   // We're assuming 'Exp' is mutated as soon as its address is taken, though in
   // theory we can follow the pointer and see whether it escaped `Stm` or is
   // dereferenced and then mutated. This is left for future improvements.
-  const auto AsAmpersandOperand =
-      unaryOperator(hasOperatorName("&"),
-                    // A NoOp implicit cast is adding const.
-                    unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
-                    hasUnaryOperand(canResolveToExpr(Exp)));
+  const auto AsAmpersandOperand = unaryOperator(
+      hasOperatorName("&"),
+      // A NoOp implicit cast is adding const.
+      unless(hasParent(implicitCastExpr(
+          anyOf(hasCastKind(CK_NoOp), 
hasCastKind(CK_FunctionPointerConversion),
+                hasCastKind(CK_MemberFunctionPointerConversion))))),
+      hasUnaryOperand(canResolveToExpr(Exp)));
   const auto AsPointerFromArrayDecay = castExpr(
       hasCastKind(CK_ArrayToPointerDecay),
       unless(hasParent(arraySubscriptExpr())), has(canResolveToExpr(Exp)));
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp 
b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 9c54eb16d22246..b58d40df7f07fc 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -322,7 +322,9 @@ class TransferVisitor : public 
ConstStmtVisitor<TransferVisitor> {
     case CK_UserDefinedConversion:
       // FIXME: Add tests that excercise CK_UncheckedDerivedToBase,
       // CK_ConstructorConversion, and CK_UserDefinedConversion.
-    case CK_NoOp: {
+    case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion: {
       // FIXME: Consider making `Environment::getStorageLocation` skip noop
       // expressions (this and other similar expressions in the file) instead
       // of assigning them storage locations.
@@ -679,7 +681,9 @@ class TransferVisitor : public 
ConstStmtVisitor<TransferVisitor> {
   }
 
   void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) {
-    if (S->getCastKind() == CK_NoOp) {
+    if (S->getCastKind() == CK_NoOp ||
+        S->getCastKind() == CK_FunctionPointerConversion ||
+        S->getCastKind() == CK_MemberFunctionPointerConversion) {
       const Expr *SubExpr = S->getSubExpr();
       assert(SubExpr != nullptr);
 
diff --git a/clang/lib/Analysis/ThreadSafety.cpp 
b/clang/lib/Analysis/ThreadSafety.cpp
index 5577f45aa5217f..8c3396e2d9da41 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -2103,7 +2103,9 @@ void BuildLockset::VisitCXXConstructExpr(const 
CXXConstructExpr *Exp) {
 
 static const Expr *UnpackConstruction(const Expr *E) {
   if (auto *CE = dyn_cast<CastExpr>(E))
-    if (CE->getCastKind() == CK_NoOp)
+    if (CE->getCastKind() == CK_NoOp ||
+        CE->getCastKind() == CK_FunctionPointerConversion ||
+        CE->getCastKind() == CK_MemberFunctionPointerConversion)
       E = CE->getSubExpr()->IgnoreParens();
   if (auto *CE = dyn_cast<CastExpr>(E))
     if (CE->getCastKind() == CK_ConstructorConversion ||
diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp 
b/clang/lib/Analysis/ThreadSafetyCommon.cpp
index cbcfefdc525490..aa54fe4ea66d5b 100644
--- a/clang/lib/Analysis/ThreadSafetyCommon.cpp
+++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp
@@ -620,6 +620,8 @@ til::SExpr *SExprBuilder::translateCastExpr(const CastExpr 
*CE,
     // return new (Arena) til::Load(E0);
   }
   case CK_NoOp:
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion:
   case CK_DerivedToBase:
   case CK_UncheckedDerivedToBase:
   case CK_ArrayToPointerDecay:
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 563f728e29d781..e035ae819eecc7 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -697,6 +697,8 @@ static bool tryEmitARCCopyWeakInit(CodeGenFunction &CGF,
     switch (castExpr->getCastKind()) {
     // Look through casts that don't require representation changes.
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
     case CK_BitCast:
     case CK_BlockPointerToObjCPointerCast:
       needsCast = true;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 9166db4c74128c..98d8bb39efd5b5 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1297,6 +1297,8 @@ static Address EmitPointerWithAlignment(const Expr *E, 
LValueBaseInfo *BaseInfo,
     // Non-converting casts (but not C's implicit conversion from void*).
     case CK_BitCast:
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
     case CK_AddressSpaceConversion:
       if (auto PtrTy = CE->getSubExpr()->getType()->getAs<PointerType>()) {
         if (PtrTy->getPointeeType()->isVoidType())
@@ -5292,6 +5294,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) 
{
   case CK_IntegralComplexToBoolean:
   case CK_IntegralComplexCast:
   case CK_IntegralComplexToFloatingComplex:
+  case CK_MemberFunctionPointerConversion:
   case CK_DerivedToBaseMemberPointer:
   case CK_BaseToDerivedMemberPointer:
   case CK_MemberPointerToBoolean:
@@ -5339,7 +5342,8 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) 
{
   case CK_LValueToRValue:
     return EmitLValue(E->getSubExpr());
 
-  case CK_NoOp: {
+  case CK_NoOp:
+  case CK_FunctionPointerConversion: {
     // CK_NoOp can model a qualification conversion, which can remove an array
     // bound and change the IR type.
     // FIXME: Once pointee types are removed from IR, remove this.
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index bbfc6672ecc25a..b30410da255e12 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -905,6 +905,8 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
   case CK_FunctionToPointerDecay:
   case CK_NullToPointer:
   case CK_NullToMemberPointer:
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion:
   case CK_BaseToDerivedMemberPointer:
   case CK_DerivedToBaseMemberPointer:
   case CK_MemberPointerToBoolean:
@@ -1424,6 +1426,8 @@ static bool castPreservesZero(const CastExpr *CE) {
   switch (CE->getCastKind()) {
     // No-ops.
   case CK_NoOp:
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion:
   case CK_UserDefinedConversion:
   case CK_ConstructorConversion:
   case CK_BitCast:
diff --git a/clang/lib/CodeGen/CGExprComplex.cpp 
b/clang/lib/CodeGen/CGExprComplex.cpp
index fef26e7b4ccdbd..794b06ebf29935 100644
--- a/clang/lib/CodeGen/CGExprComplex.cpp
+++ b/clang/lib/CodeGen/CGExprComplex.cpp
@@ -560,6 +560,8 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, 
Expr *Op,
   }
 
   case CK_BitCast:
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion:
   case CK_BaseToDerived:
   case CK_DerivedToBase:
   case CK_UncheckedDerivedToBase:
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index dd65080a840446..c76bbbfeb0fd59 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1163,6 +1163,8 @@ class ConstExprEmitter
     case CK_AtomicToNonAtomic:
     case CK_NonAtomicToAtomic:
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
     case CK_ConstructorConversion:
       return Visit(subExpr, destType);
 
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index b7f5b932c56b6f..78f4a03cb5e12b 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2418,7 +2418,9 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
   case CK_UserDefinedConversion:
     return Visit(const_cast<Expr*>(E));
 
-  case CK_NoOp: {
+  case CK_NoOp:
+  case CK_FunctionPointerConversion:
+  case CK_MemberFunctionPointerConversion: {
     return CE->changesVolatileQualification() ? EmitLoadOfLValue(CE)
                                               : Visit(const_cast<Expr *>(E));
   }
diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp 
b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
index 81797c8c4dc75a..8443f47c2199b6 100644
--- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
+++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -999,6 +999,8 @@ static bool rewriteToNumericBoxedExpression(const 
ObjCMessageExpr *Msg,
     switch (ICE->getCastKind()) {
     case CK_LValueToRValue:
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
     case CK_UserDefinedConversion:
     case CK_HLSLArrayRValue:
       break;
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp 
b/clang/lib/Sema/CheckExprLifetime.cpp
index 009b8d000e6b0e..164973591649ae 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -816,6 +816,8 @@ static void 
visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
         // We assume that casts to 'bool' do not preserve enough information to
         // retain a local object.
       case CK_NoOp:
+      case CK_FunctionPointerConversion:
+      case CK_MemberFunctionPointerConversion:
       case CK_BitCast:
       case CK_BaseToDerived:
       case CK_DerivedToBase:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index af1dc21594da8a..70fa37b295f190 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12975,6 +12975,8 @@ std::optional<std::pair<
     default:
       break;
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
       return getBaseAlignmentAndOffsetFromLValue(From, Ctx);
     case CK_UncheckedDerivedToBase:
     case CK_DerivedToBase: {
@@ -13070,6 +13072,8 @@ std::optional<std::pair<
     default:
       break;
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
       return getBaseAlignmentAndOffsetFromPtr(From, Ctx);
     case CK_ArrayToPointerDecay:
       return getBaseAlignmentAndOffsetFromLValue(From, Ctx);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 1bf0e800a36228..5a69b99d0a16a9 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -12733,7 +12733,9 @@ namespace {
           if (ILE->getNumInits() == 1)
             ArgExpr = ILE->getInit(0);
         if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgExpr))
-          if (ICE->getCastKind() == CK_NoOp)
+          if (ICE->getCastKind() == CK_NoOp ||
+              ICE->getCastKind() == CK_FunctionPointerConversion ||
+              ICE->getCastKind() == CK_MemberFunctionPointerConversion)
             ArgExpr = ICE->getSubExpr();
         HandleValue(ArgExpr);
         return;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index d8cdfcf8c6ec05..59f4d5511b19e3 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -3878,7 +3878,9 @@ namespace {
           if (ILE->getNumInits() == 1)
             ArgExpr = ILE->getInit(0);
         if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgExpr))
-          if (ICE->getCastKind() == CK_NoOp)
+          if (ICE->getCastKind() == CK_NoOp ||
+              ICE->getCastKind() == CK_FunctionPointerConversion ||
+              ICE->getCastKind() == CK_MemberFunctionPointerConversion)
             ArgExpr = ICE->getSubExpr();
         HandleValue(ArgExpr, false /*AddressOf*/);
         return;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 66df9c969256a2..79f8ea167d4f4b 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19446,6 +19446,8 @@ static ExprResult 
rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E,
     // can be found.
     switch (ICE->getCastKind()) {
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
     case CK_DerivedToBase:
     case CK_UncheckedDerivedToBase: {
       ExprResult Sub = Rebuild(ICE->getSubExpr());
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index ac3fe6ab8f9bd0..db0cfce7f3f177 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4871,7 +4871,11 @@ Sema::PerformImplicitConversion(Expr *From, QualType 
ToType,
     if (CheckExceptionSpecCompatibility(From, ToType))
       return ExprError();
 
-    From = ImpCastExprToType(From, ToType, CK_NoOp, VK_PRValue,
+    From = ImpCastExprToType(From, ToType,
+                             ToType->isMemberFunctionPointerType()
+                                 ? CK_MemberFunctionPointerConversion
+                                 : CK_FunctionPointerConversion,
+                             VK_PRValue,
                              /*BasePath=*/nullptr, CCK)
                .get();
     break;
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 4d11f2a43fcc6b..dc28b66f35f900 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8016,8 +8016,8 @@ ExprResult InitializationSequence::Perform(Sema &S,
     case SK_FunctionReferenceConversion:
       assert(CurInit.get()->isLValue() &&
              "function reference should be lvalue");
-      CurInit =
-          S.ImpCastExprToType(CurInit.get(), Step->Type, CK_NoOp, VK_LValue);
+      CurInit = S.ImpCastExprToType(CurInit.get(), Step->Type,
+                                    CK_FunctionPointerConversion, VK_LValue);
       break;
 
     case SK_AtomicConversion: {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index d304f322aced64..2e36f652baa5df 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -330,6 +330,8 @@ static const Expr *IgnoreNarrowingConversion(ASTContext 
&Ctx,
   while (auto *ICE = dyn_cast<ImplicitCastExpr>(Converted)) {
     switch (ICE->getCastKind()) {
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
     case CK_IntegralCast:
     case CK_IntegralToBoolean:
     case CK_IntegralToFloating:
@@ -14206,7 +14208,9 @@ ExprResult Sema::BuildCXXMemberCallExpr(Expr *E, 
NamedDecl *FoundDecl,
     // was a LambdaExpr.
     Expr *SubE = E;
     auto *CE = dyn_cast<CastExpr>(SubE);
-    if (CE && CE->getCastKind() == CK_NoOp)
+    if (CE && (CE->getCastKind() == CK_NoOp ||
+               CE->getCastKind() == CK_FunctionPointerConversion ||
+               CE->getCastKind() == CK_MemberFunctionPointerConversion))
       SubE = CE->getSubExpr();
     SubE = SubE->IgnoreParens();
     if (auto *BE = dyn_cast<CXXBindTemporaryExpr>(SubE))
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 9e235a46707cd4..202909924cabc8 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -282,6 +282,8 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned 
DiagID) {
   E = WarnExpr;
   if (const auto *Cast = dyn_cast<CastExpr>(E))
     if (Cast->getCastKind() == CK_NoOp ||
+        Cast->getCastKind() == CK_FunctionPointerConversion ||
+        Cast->getCastKind() == CK_MemberFunctionPointerConversion ||
         Cast->getCastKind() == CK_ConstructorConversion ||
         Cast->getCastKind() == CK_IntegralCast)
       E = Cast->getSubExpr()->IgnoreImpCasts();
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 7a900780384a91..ad021d12a7fd43 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -327,6 +327,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const 
Expr *Ex,
       case CK_NonAtomicToAtomic:
         // True no-ops.
       case CK_NoOp:
+      case CK_FunctionPointerConversion:
+      case CK_MemberFunctionPointerConversion:
       case CK_ConstructorConversion:
       case CK_UserDefinedConversion:
       case CK_FunctionToPointerDecay:
diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp 
b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index cb5fcbade2cfc2..7b0af9b9d57879 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -397,6 +397,8 @@ std::optional<SVal> SValBuilder::getConstantVal(const Expr 
*E) {
     case CK_ArrayToPointerDecay:
     case CK_IntegralToPointer:
     case CK_NoOp:
+    case CK_FunctionPointerConversion:
+    case CK_MemberFunctionPointerConversion:
     case CK_BitCast: {
       const Expr *SE = CE->getSubExpr();
       std::optional<SVal> Val = getConstantVal(SE);
diff --git a/clang/test/AST/ast-dump-function-pointer-conversion.cpp 
b/clang/test/AST/ast-dump-function-pointer-conversion.cpp
new file mode 100644
index 00000000000000..4c545e5bc40524
--- /dev/null
+++ b/clang/test/AST/ast-dump-function-pointer-conversion.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -std=c++17 -ast-dump %s | FileCheck 
--check-prefixes=CHECK,CXX17 %s
+// RUN: %clang_cc1 -std=c++11 -ast-dump %s | FileCheck %s
+
+void f() noexcept;
+
+struct S {
+  void m() noexcept;
+};
+
+// CHECK: FunctionDecl {{.*}} testFunctionPointerConversion 'void ()'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: DeclStmt
+// CHECK-NEXT: VarDecl {{.*}} fp 'void (*)()' cinit
+// CXX17-NEXT: ImplicitCastExpr {{.*}} 'void (*)()' <FunctionPointerConversion>
+// CHECK-NEXT: UnaryOperator {{.*}} 'void (*)() noexcept' prefix '&'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'void () noexcept' lvalue Function {{.*}} 
'f' 'void () noexcept'
+void testFunctionPointerConversion() {
+  void (*fp)() = &f;
+}
+
+// CHECK: FunctionDecl {{.*}} testMemberFunctionPointerConversion 'void ()'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: DeclStmt
+// CHECK-NEXT: VarDecl {{.*}} mfp 'void (S::*)()' cinit
+// CXX17-NEXT: ImplicitCastExpr {{.*}} 'void (S::*)()' 
<MemberFunctionPointerConversion>
+// CHECK-NEXT: UnaryOperator {{.*}} 'void (S::*)() noexcept' prefix '&'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'void () noexcept' CXXMethod {{.*}} 'm' 
'void () noexcept'
+void testMemberFunctionPointerConversion() {
+  void (S::*mfp)() = &S::m;
+}
diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp 
b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp
index 32c4ddd921bba1..a6209337a08864 100644
--- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp
@@ -3,7 +3,7 @@
 void f() noexcept;
 
 // CHECK: VarDecl {{.*}} ref 'void (&)()' cinit
-// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()' lvalue <NoOp>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()' lvalue 
<FunctionPointerConversion>
 // CHECK-NEXT: DeclRefExpr {{.*}} 'void () noexcept' lvalue Function {{.*}} 
'f' 'void () noexcept'
 void (&ref)() = f;
 
@@ -13,6 +13,6 @@ struct X {
 } x;
 
 // CHECK: VarDecl {{.*}} xp 'void (&)()' cinit
-// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()' lvalue <NoOp>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()' lvalue 
<FunctionPointerConversion>
 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void () noexcept' lvalue 
<UserDefinedConversion>
 void (&xp)() = x;

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to