[clang][Interp] Merge FunctionPointer into Pointer

Back when I introduced the FunctionPointer class, I assumed that we
can always know that a Pointer is not a FunctionPointer. With the
DecayPtr op, that changed somewhat, but the information whether a
Pointer was created through a FunctionPointer was lost.

However, we need this information, especially when we're in the
codegen stage.
diff --git a/clang/lib/AST/Interp/FunctionPointer.h b/clang/lib/AST/Interp/FunctionPointer.h
index 0f2c6e5..d92cd32 100644
--- a/clang/lib/AST/Interp/FunctionPointer.h
+++ b/clang/lib/AST/Interp/FunctionPointer.h
@@ -23,11 +23,10 @@
   bool Valid;
 
 public:
-  FunctionPointer(const Function *Func) : Func(Func), Valid(true) {
-    assert(Func);
-  }
+  FunctionPointer() = default;
+  FunctionPointer(const Function *Func) : Func(Func), Valid(true) {}
 
-  FunctionPointer(uintptr_t IntVal = 0, const Descriptor *Desc = nullptr)
+  FunctionPointer(uintptr_t IntVal, const Descriptor *Desc = nullptr)
       : Func(reinterpret_cast<const Function *>(IntVal)), Valid(false) {}
 
   const Function *getFunction() const { return Func; }
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 6fcd90e..bf29b85 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -333,7 +333,7 @@
 }
 
 static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
-  if (Ptr.isIntegralPointer())
+  if (!Ptr.isBlockPointer())
     return true;
   return CheckConstant(S, OpPC, Ptr.getDeclDesc());
 }
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 8e96f78..4928028 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -2811,6 +2811,13 @@
   using ToT = typename PrimConv<TOut>::T;
 
   const FromT &OldPtr = S.Stk.pop<FromT>();
+
+  if constexpr (std::is_same_v<FromT, FunctionPointer> &&
+                std::is_same_v<ToT, Pointer>) {
+    S.Stk.push<Pointer>(OldPtr.getFunction());
+    return true;
+  }
+
   S.Stk.push<ToT>(ToT(OldPtr.getIntegerRepresentation(), nullptr));
   return true;
 }
diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp
index 29579f5..3ac8bc2 100644
--- a/clang/lib/AST/Interp/Pointer.cpp
+++ b/clang/lib/AST/Interp/Pointer.cpp
@@ -55,7 +55,7 @@
 }
 
 Pointer::~Pointer() {
-  if (isIntegralPointer())
+  if (!isBlockPointer())
     return;
 
   if (Block *Pointee = PointeeStorage.BS.Pointee) {
@@ -87,6 +87,8 @@
       PointeeStorage.BS.Pointee->addPointer(this);
   } else if (P.isIntegralPointer()) {
     PointeeStorage.Int = P.PointeeStorage.Int;
+  } else if (P.isFunctionPointer()) {
+    PointeeStorage.Fn = P.PointeeStorage.Fn;
   } else {
     assert(false && "Unhandled storage kind");
   }
@@ -115,6 +117,8 @@
       PointeeStorage.BS.Pointee->addPointer(this);
   } else if (P.isIntegralPointer()) {
     PointeeStorage.Int = P.PointeeStorage.Int;
+  } else if (P.isFunctionPointer()) {
+    PointeeStorage.Fn = P.PointeeStorage.Fn;
   } else {
     assert(false && "Unhandled storage kind");
   }
@@ -131,6 +135,8 @@
                    CharUnits::fromQuantity(asIntPointer().Value + this->Offset),
                    Path,
                    /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
+  if (isFunctionPointer())
+    return asFunctionPointer().toAPValue(ASTCtx);
 
   // Build the lvalue base from the block.
   const Descriptor *Desc = getDeclDesc();
@@ -263,7 +269,7 @@
 }
 
 bool Pointer::isInitialized() const {
-  if (isIntegralPointer())
+  if (!isBlockPointer())
     return true;
 
   if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {
@@ -299,7 +305,7 @@
 }
 
 void Pointer::initialize() const {
-  if (isIntegralPointer())
+  if (!isBlockPointer())
     return;
 
   assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");
@@ -368,10 +374,15 @@
 
   if (A.isIntegralPointer() && B.isIntegralPointer())
     return true;
+  if (A.isFunctionPointer() && B.isFunctionPointer())
+    return true;
 
   if (A.isIntegralPointer() || B.isIntegralPointer())
     return A.getSource() == B.getSource();
 
+  if (A.StorageKind != B.StorageKind)
+    return false;
+
   return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;
 }
 
diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index e351699..6b0c313 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -14,6 +14,7 @@
 #define LLVM_CLANG_AST_INTERP_POINTER_H
 
 #include "Descriptor.h"
+#include "FunctionPointer.h"
 #include "InterpBlock.h"
 #include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/Decl.h"
@@ -45,7 +46,7 @@
   uint64_t Value;
 };
 
-enum class Storage { Block, Int };
+enum class Storage { Block, Int, Fn };
 
 /// A pointer to a memory block, live or dead.
 ///
@@ -96,6 +97,10 @@
     PointeeStorage.Int.Value = Address;
     PointeeStorage.Int.Desc = Desc;
   }
+  Pointer(const Function *F, uint64_t Offset = 0)
+      : Offset(Offset), StorageKind(Storage::Fn) {
+    PointeeStorage.Fn = FunctionPointer(F);
+  }
   ~Pointer();
 
   void operator=(const Pointer &P);
@@ -126,6 +131,8 @@
   uint64_t getIntegerRepresentation() const {
     if (isIntegralPointer())
       return asIntPointer().Value + (Offset * elemSize());
+    if (isFunctionPointer())
+      return asFunctionPointer().getIntegerRepresentation();
     return reinterpret_cast<uint64_t>(asBlockPointer().Pointee) + Offset;
   }
 
@@ -137,6 +144,8 @@
   [[nodiscard]] Pointer atIndex(uint64_t Idx) const {
     if (isIntegralPointer())
       return Pointer(asIntPointer().Value, asIntPointer().Desc, Idx);
+    if (isFunctionPointer())
+      return Pointer(asFunctionPointer().getFunction(), Idx);
 
     if (asBlockPointer().Base == RootPtrMark)
       return Pointer(asBlockPointer().Pointee, RootPtrMark,
@@ -247,18 +256,20 @@
   bool isZero() const {
     if (isBlockPointer())
       return asBlockPointer().Pointee == nullptr;
+    if (isFunctionPointer())
+      return asFunctionPointer().isZero();
     assert(isIntegralPointer());
     return asIntPointer().Value == 0 && Offset == 0;
   }
   /// Checks if the pointer is live.
   bool isLive() const {
-    if (isIntegralPointer())
+    if (!isBlockPointer())
       return true;
     return asBlockPointer().Pointee && !asBlockPointer().Pointee->IsDead;
   }
   /// Checks if the item is a field in an object.
   bool isField() const {
-    if (isIntegralPointer())
+    if (!isBlockPointer())
       return false;
 
     return !isRoot() && getFieldDesc()->asDecl();
@@ -268,6 +279,8 @@
   const Descriptor *getDeclDesc() const {
     if (isIntegralPointer())
       return asIntPointer().Desc;
+    if (isFunctionPointer())
+      return nullptr;
 
     assert(isBlockPointer());
     assert(asBlockPointer().Pointee);
@@ -279,7 +292,10 @@
   DeclTy getSource() const {
     if (isBlockPointer())
       return getDeclDesc()->getSource();
-
+    if (isFunctionPointer()) {
+      const Function *F = asFunctionPointer().getFunction();
+      return F ? F->getDecl() : DeclTy();
+    }
     assert(isIntegralPointer());
     return asIntPointer().Desc ? asIntPointer().Desc->getSource() : DeclTy();
   }
@@ -354,6 +370,7 @@
   /// Returns the offset into an array.
   unsigned getOffset() const {
     assert(Offset != PastEndMark && "invalid offset");
+    assert(isBlockPointer());
     if (asBlockPointer().Base == RootPtrMark)
       return Offset;
 
@@ -421,8 +438,14 @@
     assert(isIntegralPointer());
     return PointeeStorage.Int;
   }
+  [[nodiscard]] const FunctionPointer &asFunctionPointer() const {
+    assert(isFunctionPointer());
+    return PointeeStorage.Fn;
+  }
+
   bool isBlockPointer() const { return StorageKind == Storage::Block; }
   bool isIntegralPointer() const { return StorageKind == Storage::Int; }
+  bool isFunctionPointer() const { return StorageKind == Storage::Fn; }
 
   /// Returns the record descriptor of a class.
   const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
@@ -445,7 +468,7 @@
   }
   /// Checks if the storage is static.
   bool isStatic() const {
-    if (isIntegralPointer())
+    if (!isBlockPointer())
       return true;
     assert(asBlockPointer().Pointee);
     return asBlockPointer().Pointee->isStatic();
@@ -469,7 +492,7 @@
   }
 
   bool isWeak() const {
-    if (isIntegralPointer())
+    if (!isBlockPointer())
       return false;
 
     assert(isBlockPointer());
@@ -528,8 +551,8 @@
 
   /// Returns the number of elements.
   unsigned getNumElems() const {
-    if (isIntegralPointer())
-      return ~unsigned(0);
+    if (!isBlockPointer())
+      return ~0u;
     return getSize() / elemSize();
   }
 
@@ -555,7 +578,7 @@
 
   /// Checks if the index is one past end.
   bool isOnePastEnd() const {
-    if (isIntegralPointer())
+    if (isIntegralPointer() || isFunctionPointer())
       return false;
 
     if (!asBlockPointer().Pointee)
@@ -692,6 +715,7 @@
   union {
     BlockPointer BS;
     IntPointer Int;
+    FunctionPointer Fn;
   } PointeeStorage;
   Storage StorageKind = Storage::Int;
 };