[clang] [lldb] [clang]: support std::meta::info for primitive types (PR #190356)

Nhat Nguyen via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 27 17:01:58 PDT 2026


https://github.com/changkhothuychung updated https://github.com/llvm/llvm-project/pull/190356

>From d31d40d2839e716fa95d3f624ef04dc329968d49 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Thu, 23 Jul 2026 14:59:10 -0400
Subject: [PATCH 01/17] support std::meta::info for primitive types

---
 clang/include/clang/AST/APValue.h             | 43 +++++++++-
 clang/include/clang/AST/ASTContext.h          |  1 +
 clang/include/clang/AST/BuiltinTypes.def      |  3 +
 clang/include/clang/AST/ExprCXX.h             | 21 +++--
 clang/include/clang/AST/PropertiesBase.td     | 25 ++++++
 clang/include/clang/AST/Reflection.h          | 39 +++++++++
 clang/include/clang/AST/TypeBase.h            |  5 ++
 clang/include/clang/Basic/TargetInfo.h        |  6 ++
 .../include/clang/Serialization/ASTBitCodes.h |  5 +-
 clang/lib/AST/APValue.cpp                     | 79 +++++++++++++++++++
 clang/lib/AST/ASTContext.cpp                  |  9 +++
 clang/lib/AST/ASTImporter.cpp                 | 34 +++++++-
 clang/lib/AST/ByteCode/Compiler.cpp           | 20 +++++
 clang/lib/AST/ByteCode/Compiler.h             |  1 +
 clang/lib/AST/ByteCode/Context.cpp            |  3 +
 clang/lib/AST/ByteCode/Descriptor.cpp         |  1 +
 clang/lib/AST/ByteCode/Disasm.cpp             |  2 +
 clang/lib/AST/ByteCode/Interp.cpp             |  2 +-
 clang/lib/AST/ByteCode/Interp.h               |  7 ++
 .../lib/AST/ByteCode/InterpBuiltinBitCast.cpp |  2 +-
 clang/lib/AST/ByteCode/InterpStack.h          |  3 +
 clang/lib/AST/ByteCode/InterpState.cpp        |  1 +
 clang/lib/AST/ByteCode/Opcodes.td             |  9 ++-
 clang/lib/AST/ByteCode/PrimType.cpp           |  1 +
 clang/lib/AST/ByteCode/PrimType.h             |  6 ++
 clang/lib/AST/ByteCode/Program.cpp            |  1 +
 clang/lib/AST/ByteCode/Program.h              |  1 +
 clang/lib/AST/ByteCode/Reflect.h              | 60 ++++++++++++++
 clang/lib/AST/ExprCXX.cpp                     | 11 +--
 clang/lib/AST/ExprConstant.cpp                | 77 +++++++++++++++++-
 clang/lib/AST/ItaniumMangle.cpp               | 73 ++++++++++++++++-
 clang/lib/AST/MicrosoftMangle.cpp             |  5 ++
 clang/lib/AST/NSAPI.cpp                       |  1 +
 clang/lib/AST/StmtPrinter.cpp                 | 11 ++-
 clang/lib/AST/TextNodeDumper.cpp              | 16 ++++
 clang/lib/AST/Type.cpp                        | 10 +++
 clang/lib/AST/TypeLoc.cpp                     |  1 +
 clang/lib/Basic/TargetInfo.cpp                |  2 +
 clang/lib/CodeGen/CGDebugInfo.cpp             |  2 +
 clang/lib/CodeGen/CGExprConstant.cpp          |  2 +
 clang/lib/CodeGen/CodeGenModule.cpp           |  2 +
 clang/lib/CodeGen/CodeGenTypes.cpp            |  7 ++
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |  1 +
 clang/lib/CodeGen/QualTypeMapper.cpp          |  3 +
 clang/lib/Sema/SemaExpr.cpp                   | 13 ++-
 clang/lib/Sema/SemaOverload.cpp               | 23 ++++--
 clang/lib/Sema/SemaTemplate.cpp               | 17 +++-
 clang/lib/Sema/TreeTransform.h                | 14 +++-
 clang/lib/Serialization/ASTCommon.cpp         |  3 +
 clang/lib/Serialization/ASTReader.cpp         |  3 +
 clang/lib/Serialization/ASTReaderStmt.cpp     | 15 +++-
 clang/lib/Serialization/ASTWriterStmt.cpp     | 15 +++-
 .../UnifiedSymbolResolution/USRGeneration.cpp |  3 +
 .../test/AST/ast-dump-APValue-reflection.cpp  | 14 ++++
 .../reflection-emit-meta-info-itanium.cpp     |  7 ++
 ...s.cpp => reflection-emit-meta-info-ms.cpp} |  0
 .../CodeGenCXX/reflection-mangle-itanium.cpp  | 42 +++++++++-
 clang/test/PCH/reflection.cpp                 | 14 ++++
 clang/test/PCH/reflection_include.h           |  1 +
 clang/test/Sema/reflection-meta-info.fail.cpp | 56 +++++++++++++
 clang/test/Sema/reflection-meta-info.pass.cpp | 77 ++++++++++++++++++
 clang/tools/libclang/CIndex.cpp               |  1 +
 clang/unittests/AST/ASTImporterTest.cpp       | 29 +++++++
 .../TypeSystem/Clang/TypeSystemClang.cpp      |  3 +
 64 files changed, 925 insertions(+), 39 deletions(-)
 create mode 100644 clang/include/clang/AST/Reflection.h
 create mode 100644 clang/lib/AST/ByteCode/Reflect.h
 create mode 100644 clang/test/AST/ast-dump-APValue-reflection.cpp
 create mode 100644 clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp
 rename clang/test/CodeGenCXX/{reflection-mangle-ms.cpp => reflection-emit-meta-info-ms.cpp} (100%)
 create mode 100644 clang/test/PCH/reflection.cpp
 create mode 100644 clang/test/PCH/reflection_include.h
 create mode 100644 clang/test/Sema/reflection-meta-info.fail.cpp
 create mode 100644 clang/test/Sema/reflection-meta-info.pass.cpp

diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h
index 9293266a41256..2f6efc670b6e8 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_AST_APVALUE_H
 #define LLVM_CLANG_AST_APVALUE_H
 
+#include "clang/AST/Reflection.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/APFixedPoint.h"
 #include "llvm/ADT/APFloat.h"
@@ -141,7 +142,8 @@ class APValue {
     Struct,
     Union,
     MemberPointer,
-    AddrLabelDiff
+    AddrLabelDiff,
+    Reflection
   };
 
   class alignas(uint64_t) LValueBase {
@@ -315,12 +317,21 @@ class APValue {
     const AddrLabelExpr* LHSExpr;
     const AddrLabelExpr* RHSExpr;
   };
+  struct ReflectionData {
+    // OperandKind will eventually have support for
+    // TypeSourceInfo, TemplateReference, NamespaceReference, DeclRefExpr.
+    // Operand stores the opaque pointer of the reflection operand.
+    // Depending on the value of OperandKind, we can perform the
+    // corresponding cast to the associated type.
+    ReflectionKind OperandKind;
+    const void *Operand;
+  };
   struct MemberPointerData;
 
   // We ensure elsewhere that Data is big enough for LV and MemberPointerData.
-  typedef llvm::AlignedCharArrayUnion<void *, APSInt, APFloat, ComplexAPSInt,
-                                      ComplexAPFloat, Vec, Mat, Arr, StructData,
-                                      UnionData, AddrLabelDiffData>
+  typedef llvm::AlignedCharArrayUnion<
+      void *, APSInt, APFloat, ComplexAPSInt, ComplexAPFloat, Vec, Mat, Arr,
+      StructData, UnionData, AddrLabelDiffData, ReflectionData>
       DataType;
   static const size_t DataSize = sizeof(DataType);
 
@@ -415,6 +426,14 @@ class APValue {
       : Kind(None), AllowConstexprUnknown(false) {
     MakeArray(InitElts, Size);
   }
+
+  /// Creates a new Reflection APValue.
+  /// \param OperandKind The kind of reflection.
+  /// \param Operand The entity being reflected.
+  APValue(ReflectionKind OperandKind, const void *Operand) : Kind(None) {
+    MakeReflection(OperandKind, Operand);
+  }
+
   /// Creates a new struct APValue.
   /// \param UninitStruct Marker. Pass an empty UninitStruct.
   /// \param NumBases Number of bases.
@@ -498,6 +517,7 @@ class APValue {
   bool isUnion() const { return Kind == Union; }
   bool isMemberPointer() const { return Kind == MemberPointer; }
   bool isAddrLabelDiff() const { return Kind == AddrLabelDiff; }
+  bool isReflection() const { return Kind == Reflection; }
 
   void dump() const;
   void dump(raw_ostream &OS, const ASTContext &Context) const;
@@ -717,6 +737,16 @@ class APValue {
     return ((const AddrLabelDiffData *)(const char *)&Data)->RHSExpr;
   }
 
+  ReflectionKind getReflectionOperandKind() const {
+    assert(isReflection() && "Invalid accessor");
+    return ((const ReflectionData *)(const char *)&Data)->OperandKind;
+  }
+
+  const void *getReflectionOpaqueOperand() const {
+    assert(isReflection() && "Invalid accessor");
+    return ((const ReflectionData *)(const char *)&Data)->Operand;
+  }
+
   void setInt(APSInt I) {
     assert(isInt() && "Invalid accessor");
     *(APSInt *)(char *)&Data = std::move(I);
@@ -767,6 +797,11 @@ class APValue {
 
 private:
   void DestroyDataAndMakeUninit();
+  void MakeReflection(ReflectionKind OperandKind, const void *Operand) {
+    assert(isAbsent() && "Bad state change");
+    new ((void *)(char *)Data.buffer) ReflectionData{OperandKind, Operand};
+    Kind = Reflection;
+  }
   void MakeInt() {
     assert(isAbsent() && "Bad state change");
     new ((void *)&Data) APSInt(1);
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 7ed6509c3c16c..05dfec8c37586 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1351,6 +1351,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   CanQualType BFloat16Ty;
   CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3
   CanQualType VoidPtrTy, NullPtrTy;
+  CanQualType MetaInfoTy;
   CanQualType DependentTy, OverloadTy, BoundMemberTy, UnresolvedTemplateTy,
       UnknownAnyTy;
   CanQualType BuiltinFnTy;
diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def
index 444be4311a743..ee2f3f0365126 100644
--- a/clang/include/clang/AST/BuiltinTypes.def
+++ b/clang/include/clang/AST/BuiltinTypes.def
@@ -223,6 +223,9 @@ FLOATING_TYPE(Ibm128, Ibm128Ty)
 
 //===- Language-specific types --------------------------------------------===//
 
+// 'std::meta::info' in C++
+BUILTIN_TYPE(MetaInfo, MetaInfoTy)
+
 // This is the type of C++0x 'nullptr'.
 BUILTIN_TYPE(NullPtr, NullPtrTy)
 
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 757f2137c90dc..4ed388877a2ab 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -25,6 +25,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/OperationKinds.h"
+#include "clang/AST/Reflection.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/TemplateBase.h"
@@ -5502,37 +5503,47 @@ class BuiltinBitCastExpr final
 ///  - a type-id, or
 ///  - an id-expression.
 class CXXReflectExpr : public Expr {
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
 
+private:
   // TODO(Reflection): add support for TemplateReference, NamespaceReference and
   // DeclRefExpr
-  using operand_type = llvm::PointerUnion<const TypeSourceInfo *>;
+  using operand_type = llvm::PointerUnion<TypeSourceInfo *>;
 
   SourceLocation CaretCaretLoc;
+  ReflectionKind Kind;
   operand_type Operand;
 
-  CXXReflectExpr(SourceLocation CaretCaretLoc, const TypeSourceInfo *TSI);
+  CXXReflectExpr(ASTContext &C, SourceLocation CaretCaretLoc,
+                 TypeSourceInfo *TSI);
   CXXReflectExpr(EmptyShell Empty);
 
 public:
   static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc,
-                                TypeSourceInfo *TL);
+                                TypeSourceInfo *TSI);
 
   static CXXReflectExpr *CreateEmpty(ASTContext &C);
 
   SourceLocation getBeginLoc() const LLVM_READONLY {
     return llvm::TypeSwitch<operand_type, SourceLocation>(Operand)
-        .Case<const TypeSourceInfo *>(
+        .Case<TypeSourceInfo *>(
             [](auto *Ptr) { return Ptr->getTypeLoc().getBeginLoc(); });
   }
 
   SourceLocation getEndLoc() const LLVM_READONLY {
     return llvm::TypeSwitch<operand_type, SourceLocation>(Operand)
-        .Case<const TypeSourceInfo *>(
+        .Case<TypeSourceInfo *>(
             [](auto *Ptr) { return Ptr->getTypeLoc().getEndLoc(); });
   }
 
   /// Returns location of the '^^'-operator.
   SourceLocation getOperatorLoc() const { return CaretCaretLoc; }
+  ReflectionKind getKind() const { return Kind; }
+  void *getOpaqueValue() const { return Operand.getOpaqueValue(); }
+  TypeSourceInfo *getTypeSourceInfo() const {
+    return cast<TypeSourceInfo *>(Operand);
+  }
 
   child_range children() {
     // TODO(Reflection)
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index 25ef4c26a9aa1..9a5c683fbf693 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -603,6 +603,31 @@ let Class = PropertyTypeCase<APValue, "LValue"> in {
   }]>;
 }
 
+def ReflectionKind : EnumPropertyType<"ReflectionKind">;
+
+let Class = PropertyTypeCase<APValue, "Reflection"> in {
+  def : Property<"reflectionKind", ReflectionKind> {
+    let Read = [{ node.getReflectionOperandKind() }];
+  }
+  def : Property<"reflectionType", QualType> {
+    let Conditional = [{ reflectionKind == ReflectionKind::Type }];
+    let Read = [{
+      QualType::getFromOpaquePtr(node.getReflectionOpaqueOperand())
+    }];
+  }
+  def : Creator<[{
+    switch (reflectionKind) {
+    // TODO(Reflection): Add support for TypeSourceInfo, NamespaceReference
+    // TemplateReference and DeclRefExpr
+    case ReflectionKind::Null:
+      return APValue(reflectionKind, nullptr);
+    case ReflectionKind::Type:
+      return APValue(reflectionKind, (*reflectionType).getAsOpaquePtr());
+    }
+    llvm_unreachable("unimplemented or unknow reflection entities");
+  }]>;
+}
+
 // Type cases for DeclarationName.
 def : PropertyTypeKind<DeclarationName, DeclarationNameKind,
                        "node.getNameKind()">;
diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h
new file mode 100644
index 0000000000000..732aefde80885
--- /dev/null
+++ b/clang/include/clang/AST/Reflection.h
@@ -0,0 +1,39 @@
+//===--- Reflection.h - Kind of reflection operands ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares the kinds of reflection operands.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_REFLECTION_H
+#define LLVM_CLANG_AST_REFLECTION_H
+
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+
+// TODO(Reflection): Add support for Template, Namespace and DeclRefExpr.
+enum class ReflectionKind { Null, Type };
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+                                     ReflectionKind Kind) {
+  switch (Kind) {
+  case ReflectionKind::Type:
+    OS << "type";
+    break;
+  case ReflectionKind::Null:
+    OS << "null";
+    break;
+  }
+
+  return OS;
+}
+
+} // namespace clang
+
+#endif
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index c9658775f0470..4886db6998b45 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -2766,6 +2766,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isUndeducedAutoType() const;             // C++11 auto or
                                                 // C++14 decltype(auto)
   bool isTypedefNameType() const;               // typedef or alias template
+  bool isMetaInfoType() const;                  // C++26 std::meta::info
 
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
   bool is##Id##Type() const;
@@ -9051,6 +9052,10 @@ inline bool Type::isVoidType() const {
   return isSpecificBuiltinType(BuiltinType::Void);
 }
 
+inline bool Type::isMetaInfoType() const {
+  return isSpecificBuiltinType(BuiltinType::MetaInfo);
+}
+
 inline bool Type::isHalfType() const {
   // FIXME: Should we allow complex __fp16? Probably not.
   return isSpecificBuiltinType(BuiltinType::Half);
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 3aba4d261a651..dacc1d3a5d9ea 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -96,6 +96,7 @@ struct TransferrableTargetInfo {
   unsigned char FloatWidth, FloatAlign;
   unsigned char DoubleWidth, DoubleAlign;
   unsigned char LongDoubleWidth, LongDoubleAlign, Float128Align, Ibm128Align;
+  unsigned char MetaInfoWidth, MetaInfoAlign;
   unsigned char LargeArrayMinWidth, LargeArrayAlign;
   unsigned char LongWidth, LongAlign;
   unsigned char LongLongWidth, LongLongAlign;
@@ -828,6 +829,11 @@ class TargetInfo : public TransferrableTargetInfo,
   unsigned getIbm128Align() const { return Ibm128Align; }
   const llvm::fltSemantics &getIbm128Format() const { return *Ibm128Format; }
 
+  /// Returns the size of std::meta::info.
+  unsigned getMetaInfoWidth() const { return MetaInfoWidth; }
+  /// eturns the align of std::meta::info.
+  unsigned getMetaInfoAlign() const { return MetaInfoAlign; }
+
   /// Return the mangled code of long double.
   virtual const char *getLongDoubleMangling() const { return "e"; }
 
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 671341488278e..e36e6ea29810f 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1155,6 +1155,9 @@ enum PredefinedTypeIDs {
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) PREDEF_TYPE_##Id##_ID,
 #include "clang/Basic/HLSLIntangibleTypes.def"
 
+  /// C++26 std::meta::info type.
+  PREDEF_TYPE_META_INFO_ID,
+
   /// The placeholder type for unresolved templates.
   PREDEF_TYPE_UNRESOLVED_TEMPLATE,
   // Sentinel value. Considered a predefined type but not useable as one.
@@ -1166,7 +1169,7 @@ enum PredefinedTypeIDs {
 ///
 /// Type IDs for non-predefined types will start at
 /// NUM_PREDEF_TYPE_IDs.
-const unsigned NUM_PREDEF_TYPE_IDS = 529;
+const unsigned NUM_PREDEF_TYPE_IDS = 530;
 
 // Ensure we do not overrun the predefined types we reserved
 // in the enum PredefinedTypeIDs above.
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index 727e5f8c00a10..1849562952fb7 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -374,6 +374,10 @@ APValue::APValue(const APValue &RHS)
     MakeAddrLabelDiff();
     setAddrLabelDiff(RHS.getAddrLabelDiffLHS(), RHS.getAddrLabelDiffRHS());
     break;
+  case Reflection:
+    MakeReflection(RHS.getReflectionOperandKind(),
+                   RHS.getReflectionOpaqueOperand());
+    break;
   }
 }
 
@@ -429,6 +433,8 @@ void APValue::DestroyDataAndMakeUninit() {
     ((MemberPointerData *)(char *)&Data)->~MemberPointerData();
   else if (Kind == AddrLabelDiff)
     ((AddrLabelDiffData *)(char *)&Data)->~AddrLabelDiffData();
+  else if (Kind == Reflection)
+    ((ReflectionData *)(char *)&Data)->~ReflectionData();
   Kind = None;
   AllowConstexprUnknown = false;
 }
@@ -438,6 +444,7 @@ bool APValue::needsCleanup() const {
   case None:
   case Indeterminate:
   case AddrLabelDiff:
+  case Reflection:
     return false;
   case Struct:
   case Union:
@@ -486,6 +493,61 @@ static void profileIntValue(llvm::FoldingSetNodeID &ID, const llvm::APInt &V) {
     ID.AddInteger((uint32_t)V.extractBitsAsZExtValue(std::min(32u, N - I), I));
 }
 
+/// [expr.reflect] p5, if a reflect-expression R matches the form
+/// ^^reflection-name it is interpreted as such; the identifier is looked up
+/// and the representation of R is determined as follows:
+/// - if lookup finds a type alias A, R represents the type the underlying
+///   entity of A if A was introduced by the declaration of a template
+///   parameter; otherwise, R represents A.
+
+/// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id,
+/// if type-id is neither a placeholder type nor
+/// in the form of nested-name-specifier_opt template_opt simple-template-id
+/// then R represents the type denoted by the type-id
+
+// In particular, this means that e.g. '^^const Alias' is reflection of
+// a type, not an alias. For example:
+//
+// using foo = const int;
+// ^^int       // Type
+// ^^const int // Type
+// ^^foo       // Alias
+// ^^const foo // Type
+static bool isTypeAliasAsReflectionName(QualType QT) {
+  return QT.getLocalQualifiers() == Qualifiers{};
+}
+
+/// Unwrap reflected type for profiling
+static void profileTypeReflection(llvm::FoldingSetNodeID &ID, QualType QT) {
+  // TODO(Reflection)
+
+  if (isTypeAliasAsReflectionName(QT)) {
+    if (const auto *TDT = QT->getAs<TypedefType>()) {
+      ID.AddBoolean(true);
+      ID.AddPointer(TDT->getDecl()->getCanonicalDecl());
+      return;
+    }
+  }
+
+  ID.AddBoolean(false);
+  QT.getCanonicalType().Profile(ID);
+}
+
+static void profileReflection(llvm::FoldingSetNodeID &ID, APValue V) {
+  ID.AddInteger(static_cast<int>(V.getReflectionOperandKind()));
+  switch (V.getReflectionOperandKind()) {
+  case ReflectionKind::Null:
+    return;
+  case ReflectionKind::Type: {
+    const TypeSourceInfo *Info =
+        static_cast<const TypeSourceInfo *>(V.getReflectionOpaqueOperand());
+    profileTypeReflection(ID, Info->getType());
+    return;
+  }
+  }
+  assert(false && "unknown or unimplemented reflection entities");
+}
+
 void APValue::Profile(llvm::FoldingSetNodeID &ID) const {
   // Note that our profiling assumes that only APValues of the same type are
   // ever compared. As a result, we don't consider collisions that could only
@@ -632,6 +694,9 @@ void APValue::Profile(llvm::FoldingSetNodeID &ID) const {
     for (const CXXRecordDecl *D : getMemberPointerPath())
       ID.AddPointer(D);
     return;
+  case Reflection:
+    profileReflection(ID, *this);
+    return;
   }
 
   llvm_unreachable("Unknown APValue kind!");
@@ -986,6 +1051,19 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy,
     Out << " - ";
     Out << "&&" << getAddrLabelDiffRHS()->getLabel()->getName();
     return;
+  case APValue::Reflection:
+    switch (getReflectionOperandKind()) {
+    case ReflectionKind::Null:
+      Out << "std::meta::info{}";
+      break;
+    case ReflectionKind::Type: {
+      const auto *TInfo =
+          static_cast<const TypeSourceInfo *>(getReflectionOpaqueOperand());
+      Out << "^^" << TInfo->getType().stream(Policy);
+      break;
+    }
+    }
+    return;
   }
   llvm_unreachable("Unknown APValue kind!");
 }
@@ -1176,6 +1254,7 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue &V,
   case APValue::ComplexInt:
   case APValue::ComplexFloat:
   case APValue::Vector:
+  case APValue::Reflection:
   case APValue::Matrix:
     break;
 
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 2228811546c0f..c94598662915d 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1511,6 +1511,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target,
   // nullptr type (C++0x 2.14.7)
   InitBuiltinType(NullPtrTy,           BuiltinType::NullPtr);
 
+  // std::meta::info type (C++26 21.4.1)
+  InitBuiltinType(MetaInfoTy, BuiltinType::MetaInfo);
+
   // half type (OpenCL 6.1.1.1) / ARM NEON __fp16
   InitBuiltinType(HalfTy, BuiltinType::Half);
 
@@ -2366,6 +2369,10 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
       Width = Target->getPointerWidth(LangAS::Default);
       Align = Target->getPointerAlign(LangAS::Default);
       break;
+    case BuiltinType::MetaInfo:
+      Width = Target->getMetaInfoWidth();
+      Align = Target->getMetaInfoAlign();
+      break;
     case BuiltinType::ObjCId:
     case BuiltinType::ObjCClass:
     case BuiltinType::ObjCSel:
@@ -3568,6 +3575,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
     case BuiltinType::VectorPair:
     case BuiltinType::DMR1024:
     case BuiltinType::DMR2048:
+    case BuiltinType::MetaInfo:
       OS << "?";
       return;
 
@@ -9325,6 +9333,7 @@ static char getObjCEncodingForPrimitiveType(const ASTContext *C,
     case BuiltinType::OCLReserveID:
     case BuiltinType::OCLSampler:
     case BuiltinType::Dependent:
+    case BuiltinType::MetaInfo:
 #define PPC_VECTOR_TYPE(Name, Id, Size) \
     case BuiltinType::Id:
 #include "clang/Basic/PPCTypes.def"
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index db7d223d56af3..7e726eb155d38 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -686,6 +686,7 @@ namespace clang {
     ExpectedStmt VisitCXXThisExpr(CXXThisExpr *E);
     ExpectedStmt VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E);
     ExpectedStmt VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E);
+    ExpectedStmt VisitCXXReflectExpr(CXXReflectExpr *E);
     ExpectedStmt VisitMemberExpr(MemberExpr *E);
     ExpectedStmt VisitCallExpr(CallExpr *E);
     ExpectedStmt VisitLambdaExpr(LambdaExpr *LE);
@@ -8861,6 +8862,16 @@ ExpectedStmt ASTNodeImporter::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) {
                                     *ToTypeOrErr, *ToLocationOrErr);
 }
 
+ExpectedStmt ASTNodeImporter::VisitCXXReflectExpr(CXXReflectExpr *E) {
+  Error Err = Error::success();
+  auto ToOperatorLoc = importChecked(Err, E->getOperatorLoc());
+  auto ToTSI = importChecked(Err, E->getTypeSourceInfo());
+  if (Err)
+    return std::move(Err);
+
+  return CXXReflectExpr::Create(Importer.getToContext(), ToOperatorLoc, ToTSI);
+}
+
 ExpectedStmt ASTNodeImporter::VisitMemberExpr(MemberExpr *E) {
   Error Err = Error::success();
   auto ToBase = importChecked(Err, E->getBase());
@@ -10873,7 +10884,7 @@ ASTNodeImporter::ImportAPValue(const APValue &FromValue) {
     }
     break;
   }
-  case APValue::LValue:
+  case APValue::LValue: {
     APValue::LValueBase Base;
     QualType FromElemTy;
     if (FromValue.getLValueBase()) {
@@ -10944,6 +10955,27 @@ ASTNodeImporter::ImportAPValue(const APValue &FromValue) {
     } else
       Result.setLValue(Base, Offset, APValue::NoLValuePath{},
                        FromValue.isNullPointer());
+    break;
+  }
+  case APValue::Reflection: {
+    switch (FromValue.getReflectionOperandKind()) {
+    case ReflectionKind::Null:
+      Result = APValue(ReflectionKind::Null, nullptr);
+      break;
+    case ReflectionKind::Type: {
+      const auto *FromTSI = static_cast<const TypeSourceInfo *>(
+          FromValue.getReflectionOpaqueOperand());
+      QualType ImpType = importChecked(Err, FromTSI->getType());
+      if (Err)
+        return std::move(Err);
+      TypeSourceInfo *ToTSI =
+          Importer.ToContext.getTrivialTypeSourceInfo(ImpType);
+      Result = APValue(ReflectionKind::Type, ToTSI);
+      break;
+    }
+    }
+    break;
+  }
   }
   if (Err)
     return std::move(Err);
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 35667a9132680..a698c35871d2e 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -4521,6 +4521,23 @@ bool Compiler<Emitter>::VisitObjCArrayLiteral(const ObjCArrayLiteral *E) {
   return this->emitError(E);
 }
 
+template <class Emitter>
+bool Compiler<Emitter>::VisitCXXReflectExpr(const CXXReflectExpr *E) {
+  if (DiscardResult)
+    return true;
+
+  switch (E->getKind()) {
+  case ReflectionKind::Type: {
+    APValue Result(ReflectionKind::Type, E->getOpaqueValue());
+    return this->emitReflectValue(E->getKind(), E->getOpaqueValue(), E);
+  }
+  case ReflectionKind::Null:
+    llvm_unreachable("A null reflection should not reach here");
+  }
+
+  return false;
+}
+
 template <class Emitter>
 bool Compiler<Emitter>::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) {
   assert(Ctx.getLangOpts().CPlusPlus);
@@ -5038,6 +5055,8 @@ bool Compiler<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
     auto Sem = Ctx.getASTContext().getFixedPointSemantics(QT);
     return this->emitConstFixedPoint(FixedPoint::zero(Sem), E);
   }
+  case PT_Reflect:
+    return this->emitReflectValue(ReflectionKind::Null, nullptr, E);
   }
   llvm_unreachable("unknown primitive type");
 }
@@ -5267,6 +5286,7 @@ bool Compiler<Emitter>::emitConst(T Value, PrimType Ty, SourceInfo Info) {
   case PT_IntAP:
   case PT_IntAPS:
   case PT_FixedPoint:
+  case PT_Reflect:
     llvm_unreachable("Invalid integral type");
     break;
   }
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 9eb8069496099..3051ad9399266 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -232,6 +232,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
   bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
   bool VisitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E);
   bool VisitObjCArrayLiteral(const ObjCArrayLiteral *E);
+  bool VisitCXXReflectExpr(const CXXReflectExpr *E);
   bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E);
 
   // Statements.
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index b913d2a9f539c..673c6adf5f96b 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -539,6 +539,9 @@ OptPrimType Context::classify(QualType T) const {
   if (T->isFixedPointType())
     return PT_FixedPoint;
 
+  if (T->isMetaInfoType())
+    return PT_Reflect;
+
   // Vector and complex types get here.
   return std::nullopt;
 }
diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp
index fb41c98dd68cb..1118267b61295 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -16,6 +16,7 @@
 #include "Pointer.h"
 #include "PrimType.h"
 #include "Record.h"
+#include "Reflect.h"
 #include "Source.h"
 #include "clang/AST/ExprCXX.h"
 
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp
index 4caf830a0a1b4..4a2e47d427f91 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -318,6 +318,8 @@ static const char *primTypeToString(PrimType T) {
     return "MemberPtr";
   case PT_FixedPoint:
     return "FixedPoint";
+  case PT_Reflect:
+    return "Reflect";
   }
   llvm_unreachable("Unhandled PrimType");
 }
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index f59485ec306e4..ebe4a7256c7fd 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -3230,7 +3230,7 @@ constexpr bool OpReturns(Opcode Op) {
          Op == OP_RetSint64 || Op == OP_RetUint64 || Op == OP_RetIntAP ||
          Op == OP_RetIntAPS || Op == OP_RetBool || Op == OP_RetFixedPoint ||
          Op == OP_RetPtr || Op == OP_RetMemberPtr || Op == OP_RetFloat ||
-         Op == OP_EndSpeculation;
+         Op == OP_RetReflect || Op == OP_EndSpeculation;
 }
 
 #if USE_TAILCALLS
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 405f4a29ec982..63f6ffc74c237 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -29,6 +29,7 @@
 #include "MemberPointer.h"
 #include "PrimType.h"
 #include "Program.h"
+#include "Reflect.h"
 #include "State.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Expr.h"
@@ -4090,6 +4091,12 @@ inline bool CheckDestruction(InterpState &S, CodePtr OpPC) {
   return checkDestructor(S, OpPC, Ptr);
 }
 
+inline bool ReflectValue(InterpState &S, CodePtr OpPC, ReflectionKind Kind,
+                         const void *Operand) {
+  S.Stk.push<Reflect>(Kind, Operand);
+  return true;
+}
+
 inline bool IsBaseClass(InterpState &S) {
   S.Stk.push<bool>(S.Stk.peek<Pointer>().isBaseClass());
   return true;
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
index f136301c02912..35edb5988b817 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
@@ -515,7 +515,7 @@ using PrimTypeVariant =
                  Char<false>, Char<true>, Integral<16, false>,
                  Integral<16, true>, Integral<32, false>, Integral<32, true>,
                  Integral<64, false>, Integral<64, true>, IntegralAP<true>,
-                 IntegralAP<false>, Boolean, Floating>;
+                 IntegralAP<false>, Boolean, Floating, Reflect>;
 
 // NB: This implementation isn't exactly ideal, but:
 //   1) We can't just do a bitcast here since we need to be able to
diff --git a/clang/lib/AST/ByteCode/InterpStack.h b/clang/lib/AST/ByteCode/InterpStack.h
index 2c02979ee6eec..a83d173e0eb16 100644
--- a/clang/lib/AST/ByteCode/InterpStack.h
+++ b/clang/lib/AST/ByteCode/InterpStack.h
@@ -17,6 +17,7 @@
 #include "IntegralAP.h"
 #include "MemberPointer.h"
 #include "PrimType.h"
+#include "Reflect.h"
 
 namespace clang {
 namespace interp {
@@ -203,6 +204,8 @@ class InterpStack final {
       return PT_MemberPtr;
     else if constexpr (std::is_same_v<T, FixedPoint>)
       return PT_FixedPoint;
+    else if constexpr (std::is_same_v<T, Reflect>)
+      return PT_Reflect;
 
     llvm_unreachable("unknown type push()'ed into InterpStack");
   }
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index f6338cda56e34..ff1b6bada6223 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -10,6 +10,7 @@
 #include "InterpFrame.h"
 #include "InterpStack.h"
 #include "Program.h"
+#include "Reflect.h"
 #include "State.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index c2f0114c81957..891fb423a9d77 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -31,6 +31,7 @@ def Float : Type;
 def Ptr : Type;
 def MemberPtr : Type;
 def FixedPoint : Type;
+def Reflect : Type;
 
 //===----------------------------------------------------------------------===//
 // Types transferred to the interpreter.
@@ -71,6 +72,8 @@ def ArgDesc : ArgType { let Name = "const Descriptor *"; }
 def ArgPrimType : ArgType { let Name = "PrimType"; }
 def ArgEnumDecl : ArgType { let Name = "const EnumDecl *"; }
 def ArgTypePtr : ArgType { let Name = "const Type *"; }
+def ArgVoidPtr : ArgType { let Name = "const void *"; }
+def ArgReflectionKind : ArgType { let Name = "ReflectionKind"; }
 
 //===----------------------------------------------------------------------===//
 // Classes of types instructions operate on.
@@ -121,7 +124,7 @@ def NonPtrTypeClass : TypeClass {
 }
 
 def AllTypeClass : TypeClass {
-  let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types, FloatTypeClass.Types);
+  let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types, FloatTypeClass.Types, [Reflect]);
 }
 
 def ComparableTypeClass : TypeClass {
@@ -368,6 +371,10 @@ def CastMemberPtrDerivedPop : Opcode {
   let NeedsOpPC = 0;
 }
 
+def ReflectValue : Opcode {
+  let Args = [ArgReflectionKind, ArgVoidPtr];
+}
+
 def FinishInitPop : SuccessOpcode;
 def FinishInit : SuccessOpcode;
 def FinishInitActivate : SuccessOpcode;
diff --git a/clang/lib/AST/ByteCode/PrimType.cpp b/clang/lib/AST/ByteCode/PrimType.cpp
index 923233e5fb13a..0ba95bab5c6b5 100644
--- a/clang/lib/AST/ByteCode/PrimType.cpp
+++ b/clang/lib/AST/ByteCode/PrimType.cpp
@@ -14,6 +14,7 @@
 #include "IntegralAP.h"
 #include "MemberPointer.h"
 #include "Pointer.h"
+#include "Reflect.h"
 
 using namespace clang;
 using namespace clang::interp;
diff --git a/clang/lib/AST/ByteCode/PrimType.h b/clang/lib/AST/ByteCode/PrimType.h
index 8f725942fedb9..ce10ebf4835ec 100644
--- a/clang/lib/AST/ByteCode/PrimType.h
+++ b/clang/lib/AST/ByteCode/PrimType.h
@@ -26,6 +26,7 @@ class Boolean;
 class Floating;
 class MemberPointer;
 class FixedPoint;
+class Reflect;
 template <bool Signed> class IntegralAP;
 template <bool Signed> class Char;
 template <unsigned Bits, bool Signed> class Integral;
@@ -47,6 +48,7 @@ enum PrimType : uint8_t {
   PT_Float = 12,
   PT_Ptr = 13,
   PT_MemberPtr = 14,
+  PT_Reflect = 15,
 };
 
 constexpr bool isIntegerOrBoolType(PrimType T) { return T <= PT_Bool; }
@@ -193,6 +195,9 @@ template <> struct PrimConv<PT_MemberPtr> {
 template <> struct PrimConv<PT_FixedPoint> {
   using T = FixedPoint;
 };
+template <> struct PrimConv<PT_Reflect> {
+  using T = Reflect;
+};
 
 /// Returns the size of a primitive type in bytes.
 size_t primSize(PrimType Type);
@@ -238,6 +243,7 @@ static inline bool aligned(const void *P) {
       TYPE_SWITCH_CASE(PT_Ptr, B)                                              \
       TYPE_SWITCH_CASE(PT_MemberPtr, B)                                        \
       TYPE_SWITCH_CASE(PT_FixedPoint, B)                                       \
+      TYPE_SWITCH_CASE(PT_Reflect, B)                                          \
     }                                                                          \
   } while (0)
 
diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp
index 378a190184be9..5f0ca50a41f4c 100644
--- a/clang/lib/AST/ByteCode/Program.cpp
+++ b/clang/lib/AST/ByteCode/Program.cpp
@@ -12,6 +12,7 @@
 #include "Function.h"
 #include "Integral.h"
 #include "PrimType.h"
+#include "Reflect.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h
index b0ef993258637..764e5ee4b5c70 100644
--- a/clang/lib/AST/ByteCode/Program.h
+++ b/clang/lib/AST/ByteCode/Program.h
@@ -17,6 +17,7 @@
 #include "Pointer.h"
 #include "PrimType.h"
 #include "Record.h"
+#include "Reflect.h"
 #include "Source.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/Support/Allocator.h"
diff --git a/clang/lib/AST/ByteCode/Reflect.h b/clang/lib/AST/ByteCode/Reflect.h
new file mode 100644
index 0000000000000..dcd00a3bad782
--- /dev/null
+++ b/clang/lib/AST/ByteCode/Reflect.h
@@ -0,0 +1,60 @@
+//===--- Boolean.h - Wrapper for boolean types for the VM -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_REFLECT_H
+#define LLVM_CLANG_AST_INTERP_REFLECT_H
+
+#include "clang/AST/APValue.h"
+#include "clang/AST/ComparisonCategories.h"
+#include "clang/AST/Reflection.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstddef>
+#include <cstdint>
+
+namespace clang {
+namespace interp {
+
+class Reflect final {
+private:
+  ReflectionKind Kind;
+  const void *Operand;
+
+public:
+  Reflect() : Kind(ReflectionKind::Null), Operand(nullptr) {}
+  Reflect(ReflectionKind Kind, const void *Operand)
+      : Kind(Kind), Operand(Operand) {}
+
+  ComparisonCategoryResult compare(const Reflect &RHS) const {
+    llvm::FoldingSetNodeID LID, RID;
+    APValue(Kind, Operand).Profile(LID);
+    APValue(RHS.Kind, RHS.Operand).Profile(RID);
+
+    if (LID == RID)
+      return ComparisonCategoryResult::Equal;
+    return ComparisonCategoryResult::Unordered;
+  }
+
+  void print(llvm::raw_ostream &OS) const {
+    OS << "Reflect(" << Kind << ", " << Operand << ")";
+  }
+  APValue toAPValue(const ASTContext &Ctx) const {
+    return APValue(Kind, Operand);
+  }
+};
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Reflect &R) {
+  R.print(OS);
+  return OS;
+}
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 6c1cde6540d85..f77999d8bada9 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -24,6 +24,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/LambdaCapture.h"
 #include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/Reflection.h"
 #include "clang/AST/TemplateBase.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
@@ -1934,15 +1935,15 @@ TypeTraitExpr *TypeTraitExpr::CreateDeserialized(const ASTContext &C,
 CXXReflectExpr::CXXReflectExpr(EmptyShell Empty)
     : Expr(CXXReflectExprClass, Empty) {}
 
-CXXReflectExpr::CXXReflectExpr(SourceLocation CaretCaretLoc,
-                               const TypeSourceInfo *TSI)
-    : Expr(CXXReflectExprClass, TSI->getType(), VK_PRValue, OK_Ordinary),
-      CaretCaretLoc(CaretCaretLoc), Operand(TSI) {}
+CXXReflectExpr::CXXReflectExpr(ASTContext &C, SourceLocation CaretCaretLoc,
+                               TypeSourceInfo *TSI)
+    : Expr(CXXReflectExprClass, C.MetaInfoTy, VK_PRValue, OK_Ordinary),
+      CaretCaretLoc(CaretCaretLoc), Kind(ReflectionKind::Type), Operand(TSI) {}
 
 CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C,
                                        SourceLocation CaretCaretLoc,
                                        TypeSourceInfo *TSI) {
-  return new (C) CXXReflectExpr(CaretCaretLoc, TSI);
+  return new (C) CXXReflectExpr(C, CaretCaretLoc, TSI);
 }
 
 CXXReflectExpr *CXXReflectExpr::CreateEmpty(ASTContext &C) {
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 574dd8b04e779..30086822583d8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -48,6 +48,7 @@
 #include "clang/AST/OSLog.h"
 #include "clang/AST/OptionalDiagnostic.h"
 #include "clang/AST/RecordLayout.h"
+#include "clang/AST/Reflection.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
@@ -2644,6 +2645,7 @@ static bool HandleConversionToBool(const APValue &Val, bool &Result) {
   case APValue::Struct:
   case APValue::Union:
   case APValue::AddrLabelDiff:
+  case APValue::Reflection:
     return false;
   }
 
@@ -7984,7 +7986,8 @@ class APValueToBufferConverter {
     case APValue::Matrix:
     case APValue::Union:
     case APValue::MemberPointer:
-    case APValue::AddrLabelDiff: {
+    case APValue::AddrLabelDiff:
+    case APValue::Reflection: {
       Info.FFDiag(BCE->getBeginLoc(),
                   diag::note_constexpr_bit_cast_unsupported_type)
           << Ty;
@@ -11220,6 +11223,57 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
 
   return true;
 }
+
+//===----------------------------------------------------------------------===//
+// Reflection expression evaluation
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ReflectionEvaluator : public ExprEvaluatorBase<ReflectionEvaluator> {
+
+  using BaseType = ExprEvaluatorBase<ReflectionEvaluator>;
+
+  APValue &Result;
+
+public:
+  ReflectionEvaluator(EvalInfo &E, APValue &Result)
+      : ExprEvaluatorBaseTy(E), Result(Result) {}
+
+  bool Success(const APValue &V, const Expr *E) {
+    Result = V;
+    return true;
+  }
+
+  bool VisitCXXReflectExpr(const CXXReflectExpr *E);
+  bool ZeroInitialization(const Expr *E);
+};
+
+bool ReflectionEvaluator::VisitCXXReflectExpr(const CXXReflectExpr *E) {
+  switch (E->getKind()) {
+  case ReflectionKind::Null: {
+    APValue Result(ReflectionKind::Null, /*Operand=*/nullptr);
+    return Success(Result, E);
+  }
+  case ReflectionKind::Type: {
+    APValue Result(ReflectionKind::Type, E->getOpaqueValue());
+    return Success(Result, E);
+  }
+  }
+  llvm_unreachable("invalid reflection");
+}
+
+bool ReflectionEvaluator::ZeroInitialization(const Expr *E) {
+  Result = APValue(ReflectionKind::Null, /*Operand=*/nullptr);
+  return true;
+}
+
+} // end anonymous namespace
+
+static bool EvaluateReflection(const Expr *E, APValue &Result, EvalInfo &Info) {
+  assert(E->isPRValue() && E->getType()->isMetaInfoType());
+  return ReflectionEvaluator(Info, Result).Visit(E);
+}
+
 //===----------------------------------------------------------------------===//
 // Member Pointer Evaluation
 //===----------------------------------------------------------------------===//
@@ -16319,6 +16373,7 @@ GCCTypeClass EvaluateBuiltinClassifyType(QualType T,
 #include "clang/Basic/AMDGPUTypes.def"
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
 #include "clang/Basic/HLSLIntangibleTypes.def"
+    case BuiltinType::MetaInfo:
       return GCCTypeClass::None;
 
     case BuiltinType::Dependent:
@@ -19431,6 +19486,23 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     return Success(CmpResult::Equal, E);
   }
 
+  if (LHSTy->isMetaInfoType() && RHSTy->isMetaInfoType()) {
+    APValue LHSValue, RHSValue;
+    llvm::FoldingSetNodeID LID, RID;
+    if (!Evaluate(LHSValue, Info, E->getLHS()))
+      return false;
+    LHSValue.Profile(LID);
+
+    if (!Evaluate(RHSValue, Info, E->getRHS()))
+      return false;
+    RHSValue.Profile(RID);
+
+    if (LID == RID)
+      return Success(CmpResult::Equal, E);
+    else
+      return Success(CmpResult::Unequal, E);
+  }
+
   return DoAfter();
 }
 
@@ -21607,6 +21679,9 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
   } else if (T->isIntegralOrEnumerationType()) {
     if (!IntExprEvaluator(Info, Result).Visit(E))
       return false;
+  } else if (T->isMetaInfoType()) {
+    if (!EvaluateReflection(E, Result, Info))
+      return false;
   } else if (T->hasPointerRepresentation()) {
     LValue LV;
     if (!EvaluatePointer(E, LV, Info))
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 172a19f8f3537..e649369b61e04 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -575,6 +575,7 @@ class CXXNameMangler {
   void mangleFloatLiteral(QualType T, const llvm::APFloat &V);
   void mangleFixedPointLiteral();
   void mangleNullPointer(QualType T);
+  void mangleReflection(ReflectionKind Kind, const void *OpaqueOperand);
 
   void mangleMemberExprBase(const Expr *base, bool isArrow);
   void mangleMemberExpr(const Expr *base, bool isArrow,
@@ -1273,6 +1274,56 @@ void CXXNameMangler::mangleNullPointer(QualType T) {
   Out << "0E";
 }
 
+void CXXNameMangler::mangleReflection(ReflectionKind Kind,
+                                      const void *OpaqueOperand) {
+  // https://github.com/itanium-cxx-abi/cxx-abi/issues/208
+  // TODO(Reflection): add support for remaining items in the grammar below
+
+  // <reflection> ::= nu                                 # null reflection
+  //              ::= vl <expression>                    # value
+  //              ::= ob <expression>                    # object
+  //              ::= vr <variable name>                 # variable
+  //              ::= sb <sb name>                       # structured binding
+  //              ::= fn <function encoding>             # function
+  //              ::= pa [ <nonnegative number> ] _ <encoding>  # function
+  //                                                              parameter
+  //              ::= en <prefix> <unqualified-name>     # enumerator
+  //              ::= an [ <nonnegative number> ] _      # annotation
+  //              ::= ta <alias prefix>                  # type alias
+  //              ::= ty <type>                          # type
+  //              ::= dm <prefix> <unqualified-name>     # non-static data
+  //                                                       member
+  //              ::= un <prefix> [ <nonnegative number> ] _ # unnamed bit-field
+  //              ::= ct [ <prefix> ] <unqualified-name> # class template
+  //              ::= ft [ <prefix> ] <unqualified-name> # function template
+  //              ::= vt [ <prefix> ] <unqualified-name> # variable template
+  //              ::= at [ <prefix> ] <unqualified-name> # alias template
+  //              ::= co [ <prefix> ] <unqualified-name> # concept
+  //              ::= na [ <prefix> ] <unqualified-name> # namespace alias
+  //              ::= ns [ <prefix> ] <unqualified-name> # namespace
+  //              ::= ng                                 # ^^::
+  //              ::= ba [ <nonnegative number> ] _ <type> # direct base class
+  //                                                         relationship
+  //              ::= ds <type> _ [ <unqualified-name> ] _
+  //                  [ <alignment number> ] _ [ <bit-width number> ] _
+  //                  [ n ]                              # data member
+  //                  description
+
+  Out << "LDm";
+  switch (Kind) {
+  case ReflectionKind::Null:
+    Out << "nu";
+    break;
+  case ReflectionKind::Type: {
+    const auto *TSI = static_cast<const TypeSourceInfo *>(OpaqueOperand);
+    Out << "ty";
+    mangleType(TSI->getType());
+    break;
+  }
+  }
+  Out << 'E';
+}
+
 void CXXNameMangler::mangleNumber(const llvm::APSInt &Value) {
   if (Value.isSigned() && Value.isNegative()) {
     Out << 'n';
@@ -3148,10 +3199,12 @@ void CXXNameMangler::mangleType(const BuiltinType *T) {
   // UNSUPPORTED:    ::= De # IEEE 754r decimal floating point (128 bits)
   // UNSUPPORTED:    ::= Df # IEEE 754r decimal floating point (32 bits)
   //                 ::= Dh # IEEE 754r half-precision floating point (16 bits)
-  //                 ::= DF <number> _ # ISO/IEC TS 18661 binary floating point type _FloatN (N bits);
+  //                 ::= DF <number> _ # ISO/IEC TS 18661 binary floating point
+  //                 type _FloatN (N bits);
   //                 ::= Di # char32_t
   //                 ::= Ds # char16_t
   //                 ::= Dn # std::nullptr_t (i.e., decltype(nullptr))
+  //                 ::= Dm # std::meta::info (i.e., decltype(^^int))
   //                 ::= [DS] DA  # N1169 fixed-point [_Sat] T _Accum
   //                 ::= [DS] DR  # N1169 fixed-point [_Sat] T _Fract
   //                 ::= u <source-name>    # vendor extended type
@@ -3420,6 +3473,10 @@ void CXXNameMangler::mangleType(const BuiltinType *T) {
     Out << TI->getIbm128Mangling();
     break;
   }
+  case BuiltinType::MetaInfo:
+    // https://github.com/itanium-cxx-abi/cxx-abi/issues/208
+    Out << "Dm";
+    break;
   case BuiltinType::NullPtr:
     Out << "Dn";
     break;
@@ -4936,6 +4993,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   //                ::= L <pointer type> 0 E         # null pointer template argument
   //                ::= L <type> <real-part float> _ <imag-part float> E    # complex floating point literal (C99); not used by clang
   //                ::= L <mangled-name> E           # external name
+  //                ::= LDm <reflection> E           # C++26 reflection value
   // clang-format on
   QualType ImplicitlyConvertedToType;
 
@@ -5015,8 +5073,8 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
     goto recurse;
 
   case Expr::CXXReflectExprClass: {
-    // TODO(Reflection): implement this after introducing std::meta::info
-    assert(false && "unimplemented");
+    const CXXReflectExpr *RE = cast<CXXReflectExpr>(E);
+    mangleReflection(RE->getKind(), RE->getOpaqueValue());
     break;
   }
 
@@ -6554,6 +6612,9 @@ static bool isZeroInitialized(QualType T, const APValue &V) {
 
   case APValue::MemberPointer:
     return !V.getMemberPointerDecl();
+
+  case APValue::Reflection:
+    return !V.getReflectionOpaqueOperand();
   }
 
   llvm_unreachable("Unhandled APValue::ValueKind enum");
@@ -6950,6 +7011,12 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V,
     break;
   }
 
+  case APValue::Reflection: {
+    mangleReflection(V.getReflectionOperandKind(),
+                     V.getReflectionOpaqueOperand());
+    break;
+  }
+
   case APValue::MemberPointer:
     // Proposed in https://github.com/itanium-cxx-abi/cxx-abi/issues/47.
     if (!V.getMemberPointerDecl()) {
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 59732ff0fed11..06abe01553b43 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -2168,6 +2168,11 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T,
     Error("template argument (value type: fixed point)");
     return;
   }
+
+  case APValue::Reflection: {
+    Error("template argument (value type: reflection)");
+    return;
+  }
   }
 }
 
diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp
index 17f5ee5dee3d1..70d56c96f5bdf 100644
--- a/clang/lib/AST/NSAPI.cpp
+++ b/clang/lib/AST/NSAPI.cpp
@@ -471,6 +471,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const {
   case BuiltinType::OMPArrayShaping:
   case BuiltinType::OMPIterator:
   case BuiltinType::BFloat16:
+  case BuiltinType::MetaInfo:
     break;
   }
 
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 9a97d52fd3020..6647b5975e7d0 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2635,8 +2635,15 @@ void StmtPrinter::VisitCXXUnresolvedConstructExpr(
 }
 
 void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) {
-  // TODO(Reflection): Implement this.
-  assert(false && "not implemented yet");
+  // TODO(Reflection): add support for the remaining reflection kinds.
+  OS << "^^";
+  switch (S->getKind()) {
+  case ReflectionKind::Null:
+    break;
+  case ReflectionKind::Type:
+    S->getTypeSourceInfo()->getType().print(OS, Policy);
+    break;
+  }
 }
 
 void StmtPrinter::VisitCXXDependentScopeMemberExpr(
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 5d724e9d1d91e..67b268af2f4c8 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -616,6 +616,7 @@ static bool isSimpleAPValue(const APValue &Value) {
   case APValue::LValue:
   case APValue::MemberPointer:
   case APValue::AddrLabelDiff:
+  case APValue::Reflection:
     return true;
   case APValue::Vector:
   case APValue::Array:
@@ -875,6 +876,21 @@ void TextNodeDumper::Visit(const APValue &Value, QualType Ty) {
     OS << " - ";
     OS << "&&" << Value.getAddrLabelDiffRHS()->getLabel()->getName();
     return;
+  case APValue::Reflection:
+    OS << "Reflection ";
+    switch (Value.getReflectionOperandKind()) {
+    case ReflectionKind::Null:
+      OS << "std::meta::info{}";
+      break;
+    case ReflectionKind::Type: {
+      OS << "^^";
+      const TypeSourceInfo *TSI = static_cast<const TypeSourceInfo *>(
+          Value.getReflectionOpaqueOperand());
+      TSI->getType().print(OS, PrintPolicy);
+      break;
+    }
+    }
+    return;
   }
   llvm_unreachable("Unknown APValue kind!");
 }
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 42d148715bc40..2f94b4664785e 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3171,6 +3171,13 @@ bool Type::isLiteralType(const ASTContext &Ctx) const {
     return true;
   }
 
+  // C++26 [basic.types]p9:
+  // -- std::meta::info is a scalar type
+  // C++26 [basic.types]p10:
+  // -- a scalar type is a literal type
+  if (isMetaInfoType())
+    return true;
+
   // We treat _Atomic T as a literal type if T is a literal type.
   if (const auto *AT = BaseTy->getAs<AtomicType>())
     return AT->getValueType()->isLiteralType(Ctx);
@@ -3546,6 +3553,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
     return "unsigned _Accum";
   case ULongAccum:
     return "unsigned long _Accum";
+  case BuiltinType::MetaInfo:
+    return "std::meta::info";
   case BuiltinType::ShortFract:
     return "short _Fract";
   case BuiltinType::Fract:
@@ -5260,6 +5269,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
 #include "clang/Basic/HLSLIntangibleTypes.def"
     case BuiltinType::BuiltinFn:
     case BuiltinType::NullPtr:
+    case BuiltinType::MetaInfo:
     case BuiltinType::IncompleteMatrixIdx:
     case BuiltinType::ArraySection:
     case BuiltinType::OMPArrayShaping:
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index 7e72a85136966..e8c018eb92e23 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -426,6 +426,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const {
   case BuiltinType::ArraySection:
   case BuiltinType::OMPArrayShaping:
   case BuiltinType::OMPIterator:
+  case BuiltinType::MetaInfo:
     return TST_unspecified;
   }
 
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index 46b5bdecb9c40..e429f4a76e564 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -126,6 +126,8 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) {
   LongDoubleAlign = 64;
   Float128Align = 128;
   Ibm128Align = 128;
+  MetaInfoWidth = 64;
+  MetaInfoAlign = 8;
   LargeArrayMinWidth = 0;
   LargeArrayAlign = 0;
   MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 0;
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 7421733efcc24..42ad4673b792b 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1216,6 +1216,8 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) {
   case BuiltinType::SatULongFract:
     Encoding = llvm::dwarf::DW_ATE_unsigned_fixed;
     break;
+  case BuiltinType::MetaInfo:
+    llvm_unreachable("std::meta::info is consteval-only type");
   }
 
   BTName = BT->getName(CGM.getLangOpts());
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 257de0145855b..f865022259978 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2752,6 +2752,8 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType,
   }
   case APValue::MemberPointer:
     return CGM.getCXXABI().EmitMemberPointer(Value, DestType);
+  case APValue::Reflection:
+    llvm_unreachable("std::meta::info is consteval-only type");
   }
   llvm_unreachable("Unknown APValue kind");
 }
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index d1119e6c00d14..da4f27b82e475 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6366,6 +6366,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
   if (getLangOpts().OpenCL && ASTTy->isSamplerT())
     return;
 
+  // TODO(Reflection): add support for consteval-only types.
+
   // HLSL default buffer constants will be emitted during HLSLBufferDecl codegen
   if (getLangOpts().HLSL &&
       D->getType().getAddressSpace() == LangAS::hlsl_constant)
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index 55fe216580314..f559ac76f7984 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -487,6 +487,13 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
       ResultType = llvm::PointerType::getUnqual(getLLVMContext());
       break;
 
+    case BuiltinType::MetaInfo:
+      // FIXME(Reflection): once consteval-only types are supported,
+      // make this an llvm_unreachable instead because reflection
+      // should not reach here
+      ResultType = llvm::IntegerType::get(getLLVMContext(), 64);
+      break;
+
     case BuiltinType::UInt128:
     case BuiltinType::Int128:
       ResultType = llvm::IntegerType::get(getLLVMContext(), 128);
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index b17478cb7ffde..80696c846c816 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -3813,6 +3813,7 @@ static bool TypeInfoIsInStandardLibrary(const BuiltinType *Ty) {
     case BuiltinType::SatUFract:
     case BuiltinType::SatULongFract:
     case BuiltinType::BFloat16:
+    case BuiltinType::MetaInfo:
       return false;
 
     case BuiltinType::Dependent:
diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp b/clang/lib/CodeGen/QualTypeMapper.cpp
index 31d9250a48ec9..f41e1fa6c9763 100644
--- a/clang/lib/CodeGen/QualTypeMapper.cpp
+++ b/clang/lib/CodeGen/QualTypeMapper.cpp
@@ -156,6 +156,9 @@ QualTypeMapper::convertBuiltinType(const BuiltinType *BT) {
     return Builder.getIntegerType(1, getTypeAlign(QT), /*Signed=*/false,
                                   /*IsBitInt=*/false);
 
+  case BuiltinType::MetaInfo:
+    llvm::reportFatalInternalError("std::meta::info is consteval-only type");
+
   case BuiltinType::Char_S:
   case BuiltinType::Char_U:
   case BuiltinType::SChar:
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b844670543a55..5f0a275c67756 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -13040,6 +13040,13 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
         *CCT, Loc, ComparisonCategoryUsage::OperatorInExpression);
   };
 
+  if (LHSType->isMetaInfoType() && RHSType->isMetaInfoType()) {
+    if (!BinaryOperator::isEqualityOp(Opc)) {
+      return InvalidOperands(Loc, LHS, RHS);
+    }
+    return computeResultTy();
+  }
+
   if (!IsOrdered && LHSIsNull != RHSIsNull) {
     bool IsEquality = Opc == BO_EQ;
     if (RHSIsNull)
@@ -16392,7 +16399,11 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
                          << resultType << Input.get()->getSourceRange());
       }
 
-      if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) {
+      if (resultType->isScalarType() && !isScopedEnumerationType(resultType) &&
+          !resultType->isMetaInfoType()) {
+        // before C++26, scalar types are contextually converted to bool,
+        // std::meta::info is a scalar type but not an arithmetic type.
+
         // C99 6.5.3.3p1: ok, fallthrough;
         if (Context.getLangOpts().CPlusPlus) {
           // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9:
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index eafda32198f11..a2d32e7406982 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -9108,6 +9108,10 @@ class BuiltinCandidateTypeSet  {
   /// candidate set.
   bool HasNullPtrType;
 
+  /// A flag indicating whether the reflection type was present in the
+  /// candidate set.
+  bool HasReflectionType;
+
   /// Sema - The semantic analysis instance where we are building the
   /// candidate type set.
   Sema &SemaRef;
@@ -9124,11 +9128,9 @@ class BuiltinCandidateTypeSet  {
   typedef TypeSet::iterator iterator;
 
   BuiltinCandidateTypeSet(Sema &SemaRef)
-    : HasNonRecordTypes(false),
-      HasArithmeticOrEnumeralTypes(false),
-      HasNullPtrType(false),
-      SemaRef(SemaRef),
-      Context(SemaRef.Context) { }
+      : HasNonRecordTypes(false), HasArithmeticOrEnumeralTypes(false),
+        HasNullPtrType(false), HasReflectionType(false), SemaRef(SemaRef),
+        Context(SemaRef.Context) {}
 
   void AddTypesConvertedFrom(QualType Ty,
                              SourceLocation Loc,
@@ -9151,6 +9153,7 @@ class BuiltinCandidateTypeSet  {
   bool hasNonRecordTypes() { return HasNonRecordTypes; }
   bool hasArithmeticOrEnumeralTypes() { return HasArithmeticOrEnumeralTypes; }
   bool hasNullPtrType() const { return HasNullPtrType; }
+  bool hasReflectionType() const { return HasReflectionType; }
 };
 
 } // end anonymous namespace
@@ -9332,6 +9335,8 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty,
     MatrixTypes.insert(Ty);
   } else if (Ty->isNullPtrType()) {
     HasNullPtrType = true;
+  } else if (Ty->isMetaInfoType()) {
+    HasReflectionType = true;
   } else if (AllowUserConversions && TyIsRec) {
     // No conversion functions in incomplete types.
     if (!SemaRef.isCompleteType(Loc, Ty))
@@ -9811,6 +9816,14 @@ class BuiltinOperatorOverloadBuilder {
           S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
         }
       }
+
+      if (CandidateTypes[ArgIdx].hasReflectionType()) {
+        CanQualType InfoTy = S.Context.getCanonicalType(S.Context.MetaInfoTy);
+        if (AddedTypes.insert(InfoTy).second) {
+          QualType ParamTypes[2] = {InfoTy, InfoTy};
+          S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
+        }
+      }
     }
   }
 
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 0d1e01df75d94..d9a8b2fea89fe 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -8111,6 +8111,18 @@ static Expr *BuildExpressionFromIntegralTemplateArgumentValue(
   return E;
 }
 
+/// Construct a new reflect expression that refers to the given
+/// entity with the given source-location of the reflection operator.
+static ExprResult BuildExpressionFromReflection(Sema &S, const APValue &RV,
+                                                SourceLocation CaretCaretLoc) {
+  // TODO(Reflection): Add support for NamespaceReference, TemplateReference,
+  // and DeclRefExpr.
+  return CXXReflectExpr::Create(
+      S.Context, CaretCaretLoc,
+      static_cast<TypeSourceInfo *>(
+          const_cast<void *>(RV.getReflectionOpaqueOperand())));
+}
+
 static Expr *BuildExpressionFromNonTypeTemplateArgumentValue(
     Sema &S, QualType T, const APValue &Val, SourceLocation Loc) {
   auto MakeInitList = [&](ArrayRef<Expr *> Elts) -> Expr * {
@@ -8176,7 +8188,7 @@ static Expr *BuildExpressionFromNonTypeTemplateArgumentValue(
   case APValue::Indeterminate:
     llvm_unreachable("Unexpected APValue kind.");
   case APValue::LValue:
-  case APValue::MemberPointer:
+  case APValue::MemberPointer: {
     // There isn't necessarily a valid equivalent source-level syntax for
     // these; in particular, a naive lowering might violate access control.
     // So for now we lower to a ConstantExpr holding the value, wrapped around
@@ -8190,6 +8202,9 @@ static Expr *BuildExpressionFromNonTypeTemplateArgumentValue(
     auto *OVE = new (S.Context) OpaqueValueExpr(Loc, T, VK);
     return ConstantExpr::Create(S.Context, OVE, Val);
   }
+  case APValue::Reflection:
+    return BuildExpressionFromReflection(S, Val, Loc).get();
+  }
   llvm_unreachable("Unhandled APValue::ValueKind enum");
 }
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index cc126f9000717..a32fccfc55ff4 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13395,7 +13395,19 @@ StmtResult TreeTransform<Derived>::TransformUnresolvedSYCLKernelCallStmt(
 template <typename Derived>
 ExprResult TreeTransform<Derived>::TransformCXXReflectExpr(CXXReflectExpr *E) {
   // TODO(reflection): Implement its transform
-  assert(false && "not implemented yet");
+
+  switch (E->getKind()) {
+  case ReflectionKind::Type: {
+    TypeSourceInfo *NewT = getDerived().TransformType(E->getTypeSourceInfo());
+    if (!NewT)
+      return ExprError();
+    return SemaRef.BuildCXXReflectExpr(E->getOperatorLoc(), NewT);
+  }
+  case ReflectionKind::Null:
+    llvm_unreachable("A null reflection should not reach here");
+  }
+
+  assert(false && "unknown or unimplemented reflection entities");
   return ExprError();
 }
 
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index 74265d5fce908..0dae1d6a35c8b 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -286,6 +286,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) {
   case BuiltinType::BFloat16:
     ID = PREDEF_TYPE_BFLOAT16_ID;
     break;
+  case BuiltinType::MetaInfo:
+    ID = PREDEF_TYPE_META_INFO_ID;
+    break;
   }
 
   return TypeIdx(0, ID);
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index a018a378e9f44..9e690dadd25b9 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -8058,6 +8058,9 @@ QualType ASTReader::GetType(TypeID ID) {
     case PREDEF_TYPE_NULLPTR_ID:
       T = Context.NullPtrTy;
       break;
+    case PREDEF_TYPE_META_INFO_ID:
+      T = Context.MetaInfoTy;
+      break;
     case PREDEF_TYPE_CHAR8_ID:
       T = Context.Char8Ty;
       break;
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 87cec16a76323..4ded925d08a38 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -540,8 +540,19 @@ void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) {
 }
 
 void ASTStmtReader::VisitCXXReflectExpr(CXXReflectExpr *E) {
-  // TODO(Reflection): Implement this.
-  assert(false && "not implemented yet");
+  // TODO(Reflection): add support for TemplateReference, NamespaceReference and
+  // DeclRefExpr
+  VisitExpr(E);
+  E->CaretCaretLoc = readSourceLocation();
+  E->Kind = static_cast<ReflectionKind>(Record.readInt());
+  switch (E->Kind) {
+  case ReflectionKind::Null:
+    E->Operand = nullptr;
+    break;
+  case ReflectionKind::Type:
+    E->Operand = Record.readTypeSourceInfo();
+    break;
+  }
 }
 
 void ASTStmtReader::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 70477f4cf4001..476ffe8726c66 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -475,8 +475,19 @@ void ASTStmtWriter::VisitCoyieldExpr(CoyieldExpr *E) {
 }
 
 void ASTStmtWriter::VisitCXXReflectExpr(CXXReflectExpr *E) {
-  // TODO(Reflection): Implement this.
-  assert(false && "not implemented yet");
+  // TODO(Reflection): add support for TemplateReference, NamespaceReference and
+  // DeclRefExpr
+  VisitExpr(E);
+  Record.AddSourceLocation(E->getOperatorLoc());
+  Record.push_back(static_cast<uint64_t>(E->getKind()));
+  switch (E->getKind()) {
+  case ReflectionKind::Null:
+    break;
+  case ReflectionKind::Type:
+    Record.AddTypeSourceInfo(E->getTypeSourceInfo());
+    break;
+  }
+  Code = serialization::EXPR_REFLECT;
 }
 
 void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) {
diff --git a/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp b/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp
index 5f689ad32159b..99901e9601ac0 100644
--- a/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp
+++ b/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp
@@ -805,6 +805,9 @@ void USRGenerator::VisitType(QualType T) {
       case BuiltinType::NullPtr:
         Out << 'n';
         break;
+      case BuiltinType::MetaInfo:
+        Out << "@BT at MetaInfo";
+        break;
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \
   case BuiltinType::Id:                                                        \
     Out << "@BT@" << #Suffix << "_" << #ImgType;                               \
diff --git a/clang/test/AST/ast-dump-APValue-reflection.cpp b/clang/test/AST/ast-dump-APValue-reflection.cpp
new file mode 100644
index 0000000000000..750cdf94da7dc
--- /dev/null
+++ b/clang/test/AST/ast-dump-APValue-reflection.cpp
@@ -0,0 +1,14 @@
+// Test without serialization:
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++26 -freflection \
+// RUN:            -ast-dump %s -ast-dump-filter Test \
+// RUN: | FileCheck --strict-whitespace %s
+
+void TestReflection() {
+  constexpr auto x = ^^int;
+  // CHECK:  | `-VarDecl {{.*}} x 'const std::meta::info' constexpr cinit
+  // CHECK-NEXT:  |   |-value: Reflection ^^int
+
+  constexpr decltype(^^int) y{};
+  // CHECK:    `-VarDecl {{.*}} y {{.*}} constexpr listinit
+  // CHECK-NEXT:        |-value: Reflection std::meta::info{}
+}
diff --git a/clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp b/clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp
new file mode 100644
index 0000000000000..a6266165e77f8
--- /dev/null
+++ b/clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -std=c++26 -freflection -triple x86_64-unknown-linux-gnu \
+// RUN:   -emit-llvm -o - %s -verify
+
+int main() {
+  (void)(^^int); // expected-error {{cannot compile this scalar expression yet}}
+  return 0;
+}
diff --git a/clang/test/CodeGenCXX/reflection-mangle-ms.cpp b/clang/test/CodeGenCXX/reflection-emit-meta-info-ms.cpp
similarity index 100%
rename from clang/test/CodeGenCXX/reflection-mangle-ms.cpp
rename to clang/test/CodeGenCXX/reflection-emit-meta-info-ms.cpp
diff --git a/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp
index a6266165e77f8..b7c18b721f976 100644
--- a/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp
+++ b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp
@@ -1,7 +1,45 @@
 // RUN: %clang_cc1 -std=c++26 -freflection -triple x86_64-unknown-linux-gnu \
-// RUN:   -emit-llvm -o - %s -verify
+// RUN:   -emit-llvm -o - %s | FileCheck %s
+
+using info = decltype(^^int);
+
+template <auto A>
+void foo () {}
 
 int main() {
-  (void)(^^int); // expected-error {{cannot compile this scalar expression yet}}
+  foo <info {}> ();
+  // CHECK: @_Z3fooITnDaLDmnuEEvv
+  foo <^^void> ();
+  // CHECK: @_Z3fooITnDaLDmtyvEEvv
+  foo <^^bool> ();
+  // CHECK: @_Z3fooITnDaLDmtybEEvv
+  foo <^^char> ();
+  // CHECK: @_Z3fooITnDaLDmtycEEvv
+  foo <^^signed char> ();
+  // CHECK: @_Z3fooITnDaLDmtyaEEvv
+  foo <^^unsigned char> ();
+  // CHECK: @_Z3fooITnDaLDmtyhEEvv
+  foo <^^short> ();
+  // CHECK: @_Z3fooITnDaLDmtysEEvv
+  foo <^^unsigned short> ();
+  // CHECK: @_Z3fooITnDaLDmtytEEvv
+  foo <^^int> ();
+  // CHECK: @_Z3fooITnDaLDmtyiEEvv
+  foo <^^unsigned int> ();
+  // CHECK: @_Z3fooITnDaLDmtyjEEvv
+  foo <^^long> ();
+  // CHECK: @_Z3fooITnDaLDmtylEEvv
+  foo <^^unsigned long> ();
+  // CHECK: @_Z3fooITnDaLDmtymEEvv
+  foo <^^long long> ();
+  // CHECK: @_Z3fooITnDaLDmtyxEEvv
+  foo <^^unsigned long long> ();
+  // CHECK: @_Z3fooITnDaLDmtyyEEvv
+  foo <^^float> ();
+  // CHECK: @_Z3fooITnDaLDmtyfEEvv
+  foo <^^double> ();
+  // CHECK: @_Z3fooITnDaLDmtydEEvv
+  foo <^^long double> ();
+  // CHECK: @_Z3fooITnDaLDmtyeEEvv
   return 0;
 }
diff --git a/clang/test/PCH/reflection.cpp b/clang/test/PCH/reflection.cpp
new file mode 100644
index 0000000000000..73e98ea68c7ba
--- /dev/null
+++ b/clang/test/PCH/reflection.cpp
@@ -0,0 +1,14 @@
+// Test without PCH
+// RUN: %clang_cc1 -std=c++26 -freflection %s -include %S/reflection_include.h -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++26 -freflection -x c++-header %S/reflection_include.h -emit-pch -o %t
+// RUN: %clang_cc1 -std=c++26 -freflection -include-pch %t -verify %s
+
+// expected-no-diagnostics
+
+
+static_assert(^^int == ^^int);
+static_assert(^^int != ^^double);
+static_assert(info{} != ^^int);
+static_assert(__is_same(decltype(^^int), info));
+static_assert(__is_same(decltype(^^double), info));
diff --git a/clang/test/PCH/reflection_include.h b/clang/test/PCH/reflection_include.h
new file mode 100644
index 0000000000000..782aff5bbd3fe
--- /dev/null
+++ b/clang/test/PCH/reflection_include.h
@@ -0,0 +1 @@
+using info = decltype(^^int);
diff --git a/clang/test/Sema/reflection-meta-info.fail.cpp b/clang/test/Sema/reflection-meta-info.fail.cpp
new file mode 100644
index 0000000000000..e4332990f5e29
--- /dev/null
+++ b/clang/test/Sema/reflection-meta-info.fail.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fexperimental-new-constant-interpreter -fsyntax-only -verify
+
+using info = decltype(^^int);
+
+struct X { int a; }; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'std::meta::info' to 'const X' for 1st argument}} \
+                     // expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'std::meta::info' to 'X' for 1st argument}} \
+                     // expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}}
+template <typename T = X, auto ptm = &X::a>
+constexpr auto ptmOp = ((T)(^^int)).*ptm; // expected-error {{no matching conversion for C-style cast from 'std::meta::info' to 'X'}}
+constexpr auto var = ptmOp<>; // expected-note {{in instantiation of variable template specialization 'ptmOp' requested here}}
+
+static_assert(^^int == ^^float); // expected-error {{static assertion failed due to requirement '^^int == ^^float'}}
+static_assert(info{} == ^^int); // expected-error {{static assertion failed due to requirement 'std::meta::info{} == ^^int'}}
+
+consteval void test()
+{
+    (^^char)++; // expected-error {{cannot increment value of type 'std::meta::info'}}
+    (^^short)++; // expected-error {{cannot increment value of type 'std::meta::info'}}
+    (^^int)++; // expected-error {{cannot increment value of type 'std::meta::info'}}
+    (^^char)--; // expected-error {{cannot decrement value of type 'std::meta::info'}}
+    (^^short)--; // expected-error {{cannot decrement value of type 'std::meta::info'}}
+    (^^int)--; // expected-error {{cannot decrement value of type 'std::meta::info'}}
+
+    ++(^^char); // expected-error {{cannot increment value of type 'std::meta::info'}}
+    ++(^^short); // expected-error {{cannot increment value of type 'std::meta::info'}}
+    ++(^^int); // expected-error {{cannot increment value of type 'std::meta::info'}}
+    --(^^char); // expected-error {{cannot decrement value of type 'std::meta::info'}}
+    --(^^short); // expected-error {{cannot decrement value of type 'std::meta::info'}}
+    --(^^int); // expected-error {{cannot decrement value of type 'std::meta::info'}}
+
+    ~(^^int);  // expected-error {{invalid argument type 'std::meta::info' to unary expression}}
+    !(^^float);  // expected-error {{invalid argument type 'std::meta::info' to unary expression}}
+
+    (^^int) + (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^double) - (^^float); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^char) * (^^short); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^long) / (^^long); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+
+    (^^int) & (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) | (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) ^ (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+
+    (^^int) < (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) > (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) <= (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) >= (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) <=>(^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+
+    // expected-error at +2 {{value of type 'std::meta::info' is not contextually convertible to 'bool'}}
+    // expected-error at +1 {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) && (^^int);
+    // expected-error at +2 {{value of type 'std::meta::info' is not contextually convertible to 'bool'}}
+    // expected-error at +1 {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) || (^^int);
+}
diff --git a/clang/test/Sema/reflection-meta-info.pass.cpp b/clang/test/Sema/reflection-meta-info.pass.cpp
new file mode 100644
index 0000000000000..46b2420af43f6
--- /dev/null
+++ b/clang/test/Sema/reflection-meta-info.pass.cpp
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fsyntax-only
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fexperimental-new-constant-interpreter -fsyntax-only
+
+typedef int int32_t;
+using A = int;
+
+using info = decltype(^^int);
+
+template <auto R>
+consteval auto f1() {
+  return R;
+}
+
+template <decltype(^^int) R>
+consteval auto f2() {
+  return R;
+}
+
+consteval void test()
+{
+    constexpr auto r = ^^int;
+    constexpr auto q = ^^float;
+    constexpr info v{};
+
+    static_assert(__is_same(decltype(^^int), info));
+    static_assert(__is_same(decltype(^^float), info));
+    static_assert(__is_same(decltype(^^double), info));
+    static_assert(__is_same(decltype(^^long), info));
+    static_assert(__is_same(decltype(^^long long), info));
+    static_assert(__is_same(decltype(^^short), info));
+    static_assert(__is_same(decltype(^^char), info));
+    static_assert(__is_same(decltype(^^unsigned char), info));
+    static_assert(__is_same(decltype(^^unsigned short), info));
+    static_assert(__is_same(decltype(^^unsigned int), info));
+    static_assert(__is_same(decltype(^^unsigned long), info));
+    static_assert(__is_same(decltype(^^unsigned long long), info));
+    static_assert(__is_same(decltype(^^info), info));
+
+    static_assert(__is_same(decltype(^^int), decltype(^^int)));
+    static_assert(__is_same(decltype(^^int), decltype(^^float)));
+    static_assert(__is_same(decltype(^^int), decltype(^^char)));
+    static_assert(__is_same(decltype(^^double), decltype(^^float)));
+
+    static_assert(!__is_same(decltype(^^int), int));
+    static_assert(__is_scalar(info));
+
+    static_assert(f1< ^^int >() == ^^int);
+    static_assert(f1< ^^A >() != ^^int);
+    static_assert(f1< ^^float>() != ^^int);
+
+    static_assert(f2<r>() == ^^int);
+    static_assert(f2<^^float>() != ^^int);
+
+    static_assert(sizeof(info) == 8);
+    static_assert(alignof(info) == 1);
+    static_assert(sizeof(decltype(^^int)) == sizeof(decltype(^^float)));
+    static_assert(^^int32_t != ^^int);
+    static_assert(^^const int != ^^int);
+    static_assert(^^volatile int32_t == ^^volatile int);
+    static_assert(^^const volatile int32_t == ^^const volatile int);
+    static_assert(^^A != ^^int);
+
+
+    static_assert(^^int == ^^int);
+    static_assert(^^int != ^^float);
+    static_assert(^^float != ^^int);
+    static_assert(!(^^float == ^^int));
+    static_assert(r != q);
+
+    int a;
+    static_assert(^^int == ^^decltype(a));
+
+    using foo = const int;
+    static_assert(^^foo != ^^const int);
+    static_assert(^^const foo == ^^const int);
+    static_assert(^^const int == ^^int const);
+}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 6e30ffd6b322d..bf641c27f8c1c 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1563,6 +1563,7 @@ bool CursorVisitor::VisitBuiltinTypeLoc(BuiltinTypeLoc TL) {
 
   case BuiltinType::Void:
   case BuiltinType::NullPtr:
+  case BuiltinType::MetaInfo:
   case BuiltinType::Dependent:
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \
   case BuiltinType::Id:
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 503f5da8af90f..592974804aad5 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -696,6 +696,32 @@ TEST_P(ImportType, ImportUsingType) {
                  cxxNewExpr(hasType(pointerType(pointee(usingType())))))));
 }
 
+struct ImportReflection : ASTImporterOptionSpecificTestBase {
+  std::vector<std::string> getExtraArgs() const override {
+    return {"-Xclang", "-freflection"};
+  }
+};
+
+TEST_P(ImportReflection, ImportMetaInfoBuiltinType) {
+  Decl *FromTU = getTuDecl("using declToImport = decltype(^^int);", Lang_CXX26,
+                           "input.cc");
+  auto *FromTA = FirstDeclMatcher<TypeAliasDecl>().match(
+      FromTU, typeAliasDecl(hasName("declToImport")));
+  ASSERT_TRUE(FromTA);
+
+  const auto *FromBT =
+      FromTA->getUnderlyingType().getCanonicalType()->getAs<BuiltinType>();
+  ASSERT_TRUE(FromBT);
+  ASSERT_EQ(BuiltinType::MetaInfo, FromBT->getKind());
+
+  auto *ToTA = Import(FromTA, Lang_CXX26);
+  ASSERT_TRUE(ToTA);
+  const auto *ToBT =
+      ToTA->getUnderlyingType().getCanonicalType()->getAs<BuiltinType>();
+  ASSERT_TRUE(ToBT);
+  EXPECT_EQ(BuiltinType::MetaInfo, ToBT->getKind());
+}
+
 TEST_P(ImportDecl, ImportFunctionTemplateDecl) {
   MatchVerifier<Decl> Verifier;
   testImport("template <typename T> void declToImport() { };", Lang_CXX03, "",
@@ -10873,6 +10899,9 @@ INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportInjectedClassNameType,
 INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportMatrixType,
                          DefaultTestValuesForRunOptions);
 
+INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportReflection,
+                         DefaultTestValuesForRunOptions);
+
 INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportTemplateParmDeclDefaultValue,
                          DefaultTestValuesForRunOptions);
 
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 2bfa187c009a7..07d08e181eae0 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -4904,6 +4904,9 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type) {
     case clang::BuiltinType::NullPtr:
       return lldb::eEncodingUint;
 
+    case clang::BuiltinType::MetaInfo:
+      return lldb::eEncodingUint;
+
     case clang::BuiltinType::Kind::ARCUnbridgedCast:
     case clang::BuiltinType::Kind::BoundMember:
     case clang::BuiltinType::Kind::BuiltinFn:

>From 14b7c6678c7539ecbfd5edb3dba49a3921df2783 Mon Sep 17 00:00:00 2001
From: Nhat Nguyen <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 11:59:19 -0400
Subject: [PATCH 02/17] Update clang/include/clang/Basic/TargetInfo.h

Co-authored-by: Sirraide <aeternalmail at gmail.com>
---
 clang/include/clang/Basic/TargetInfo.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index dacc1d3a5d9ea..892ee746a00fb 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -831,7 +831,7 @@ class TargetInfo : public TransferrableTargetInfo,
 
   /// Returns the size of std::meta::info.
   unsigned getMetaInfoWidth() const { return MetaInfoWidth; }
-  /// eturns the align of std::meta::info.
+  /// Returns the align of std::meta::info.
   unsigned getMetaInfoAlign() const { return MetaInfoAlign; }
 
   /// Return the mangled code of long double.

>From 72e47d0f1b04c2ccc1bf705c5ff6cd4b35732462 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 14:41:14 -0400
Subject: [PATCH 03/17] use MetaInfoTy directly

---
 clang/lib/Sema/SemaOverload.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index a2d32e7406982..e43e91ced930e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -9818,9 +9818,9 @@ class BuiltinOperatorOverloadBuilder {
       }
 
       if (CandidateTypes[ArgIdx].hasReflectionType()) {
-        CanQualType InfoTy = S.Context.getCanonicalType(S.Context.MetaInfoTy);
-        if (AddedTypes.insert(InfoTy).second) {
-          QualType ParamTypes[2] = {InfoTy, InfoTy};
+        CanQualType MetaInfoTy = S.Context.MetaInfoTy;
+        if (AddedTypes.insert(MetaInfoTy).second) {
+          QualType ParamTypes[2] = {MetaInfoTy, MetaInfoTy};
           S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
         }
       }

>From 923e856de5df3b3dfcb97f46af2c5bc03abb6d2b Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 14:44:03 -0400
Subject: [PATCH 04/17] fix comment

---
 clang/lib/Sema/SemaExpr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 5f0a275c67756..3cc4cd0274ef3 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -16401,7 +16401,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
 
       if (resultType->isScalarType() && !isScopedEnumerationType(resultType) &&
           !resultType->isMetaInfoType()) {
-        // before C++26, scalar types are contextually converted to bool,
+        // Before C++26, scalar types are contextually converted to bool,
         // std::meta::info is a scalar type but not an arithmetic type.
 
         // C99 6.5.3.3p1: ok, fallthrough;

>From f380a4ae694a9b2ed9fa1d140a2ac7bfe4b5151f Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 17:27:31 -0400
Subject: [PATCH 05/17] update comment about null reflection

---
 clang/include/clang/AST/APValue.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h
index 2f6efc670b6e8..3308b01e5bcad 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -319,10 +319,12 @@ class APValue {
   };
   struct ReflectionData {
     // OperandKind will eventually have support for
-    // TypeSourceInfo, TemplateReference, NamespaceReference, DeclRefExpr.
+    // Null, TypeSourceInfo, TemplateReference, NamespaceReference, DeclRefExpr.
     // Operand stores the opaque pointer of the reflection operand.
     // Depending on the value of OperandKind, we can perform the
     // corresponding cast to the associated type.
+    // If OperandKind is Null, then the ReflectionData represents
+    // a null reflection, and therefore Operand should be a nullptr.
     ReflectionKind OperandKind;
     const void *Operand;
   };

>From 3bb0c132f04c0ea88d35740bd3fc8f8a3d5b08b0 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 17:27:48 -0400
Subject: [PATCH 06/17] const ref instead of copy

---
 clang/lib/AST/APValue.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index 1849562952fb7..27c0db87cf107 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -513,7 +513,7 @@ static void profileIntValue(llvm::FoldingSetNodeID &ID, const llvm::APInt &V) {
 // ^^const int // Type
 // ^^foo       // Alias
 // ^^const foo // Type
-static bool isTypeAliasAsReflectionName(QualType QT) {
+static bool isTypeAliasAsReflectionName(const QualType& QT) {
   return QT.getLocalQualifiers() == Qualifiers{};
 }
 

>From be81c8a32f44c263426897caac653228e03d0479 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 17:28:05 -0400
Subject: [PATCH 07/17] update for typedef mangle

---
 clang/lib/AST/ItaniumMangle.cpp | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index e649369b61e04..0bd243d126d13 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1309,6 +1309,10 @@ void CXXNameMangler::mangleReflection(ReflectionKind Kind,
   //                  [ n ]                              # data member
   //                  description
 
+  auto  isTypeAliasAsReflectionName = [](const QualType& QT) {
+    return QT.getLocalQualifiers() == Qualifiers{};
+  };
+
   Out << "LDm";
   switch (Kind) {
   case ReflectionKind::Null:
@@ -1316,8 +1320,18 @@ void CXXNameMangler::mangleReflection(ReflectionKind Kind,
     break;
   case ReflectionKind::Type: {
     const auto *TSI = static_cast<const TypeSourceInfo *>(OpaqueOperand);
+    QualType QT = TSI->getType();
+
+    if (isTypeAliasAsReflectionName(QT)) {
+      if (const auto *TDT = QT->getAs<TypedefType>()) {
+        Out << "ta";
+        mangleName(TDT->getDecl()->getCanonicalDecl());
+        break;
+      }
+    }
+
     Out << "ty";
-    mangleType(TSI->getType());
+    mangleType(QT);
     break;
   }
   }

>From 6843d20217914b1655398157c216f810aa5a24e0 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 17:28:18 -0400
Subject: [PATCH 08/17] mangle test for typedef

---
 clang/test/CodeGenCXX/reflection-mangle-itanium.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp
index b7c18b721f976..e07731cc5b585 100644
--- a/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp
+++ b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp
@@ -2,6 +2,7 @@
 // RUN:   -emit-llvm -o - %s | FileCheck %s
 
 using info = decltype(^^int);
+using Type = int;
 
 template <auto A>
 void foo () {}
@@ -41,5 +42,7 @@ int main() {
   // CHECK: @_Z3fooITnDaLDmtydEEvv
   foo <^^long double> ();
   // CHECK: @_Z3fooITnDaLDmtyeEEvv
+  foo <^^Type> ();
+  // CHECK: @_Z3fooITnDaLDmta4TypeEEvv
   return 0;
 }

>From 58a87d0aeec633df8bb723b19e0f4de8642a0c54 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 17:28:39 -0400
Subject: [PATCH 09/17] serialization test

---
 clang/test/AST/ast-dump-APValue-reflection.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/clang/test/AST/ast-dump-APValue-reflection.cpp b/clang/test/AST/ast-dump-APValue-reflection.cpp
index 750cdf94da7dc..55fee355ebf1e 100644
--- a/clang/test/AST/ast-dump-APValue-reflection.cpp
+++ b/clang/test/AST/ast-dump-APValue-reflection.cpp
@@ -2,6 +2,13 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++26 -freflection \
 // RUN:            -ast-dump %s -ast-dump-filter Test \
 // RUN: | FileCheck --strict-whitespace %s
+//
+// Test with serialization:
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++26 -freflection -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -triple x86_64-unknown-unknown -std=c++26 -freflection \
+// RUN:            -include-pch %t -ast-dump-all -ast-dump-filter Test /dev/null \
+// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
+// RUN: | FileCheck --strict-whitespace %s
 
 void TestReflection() {
   constexpr auto x = ^^int;

>From 8a227668361f5a628768ca447ecb996df0faa236 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 17:37:58 -0400
Subject: [PATCH 10/17] move isTypeAliasAsReflectionName to Reflection.h

---
 clang/include/clang/AST/Reflection.h | 25 +++++++++++++++++++++++++
 clang/lib/AST/APValue.cpp            | 24 ------------------------
 clang/lib/AST/ItaniumMangle.cpp      |  4 ----
 3 files changed, 25 insertions(+), 28 deletions(-)

diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h
index 732aefde80885..d77fb450c141f 100644
--- a/clang/include/clang/AST/Reflection.h
+++ b/clang/include/clang/AST/Reflection.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_AST_REFLECTION_H
 #define LLVM_CLANG_AST_REFLECTION_H
 
+#include "clang/AST/Type.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang {
@@ -34,6 +35,30 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
   return OS;
 }
 
+/// [expr.reflect] p5, if a reflect-expression R matches the form
+/// ^^reflection-name it is interpreted as such; the identifier is looked up
+/// and the representation of R is determined as follows:
+/// - if lookup finds a type alias A, R represents the type the underlying
+///   entity of A if A was introduced by the declaration of a template
+///   parameter; otherwise, R represents A.
+
+/// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id,
+/// if type-id is neither a placeholder type nor
+/// in the form of nested-name-specifier_opt template_opt simple-template-id
+/// then R represents the type denoted by the type-id
+
+// In particular, this means that e.g. '^^const Alias' is reflection of
+// a type, not an alias. For example:
+//
+// using foo = const int;
+// ^^int       // Type
+// ^^const int // Type
+// ^^foo       // Alias
+// ^^const foo // Type
+inline bool isTypeAliasAsReflectionName(QualType QT) {
+  return QT.getLocalQualifiers() == Qualifiers{};
+}
+
 } // namespace clang
 
 #endif
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index 27c0db87cf107..7fa843b89b4d4 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -493,30 +493,6 @@ static void profileIntValue(llvm::FoldingSetNodeID &ID, const llvm::APInt &V) {
     ID.AddInteger((uint32_t)V.extractBitsAsZExtValue(std::min(32u, N - I), I));
 }
 
-/// [expr.reflect] p5, if a reflect-expression R matches the form
-/// ^^reflection-name it is interpreted as such; the identifier is looked up
-/// and the representation of R is determined as follows:
-/// - if lookup finds a type alias A, R represents the type the underlying
-///   entity of A if A was introduced by the declaration of a template
-///   parameter; otherwise, R represents A.
-
-/// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id,
-/// if type-id is neither a placeholder type nor
-/// in the form of nested-name-specifier_opt template_opt simple-template-id
-/// then R represents the type denoted by the type-id
-
-// In particular, this means that e.g. '^^const Alias' is reflection of
-// a type, not an alias. For example:
-//
-// using foo = const int;
-// ^^int       // Type
-// ^^const int // Type
-// ^^foo       // Alias
-// ^^const foo // Type
-static bool isTypeAliasAsReflectionName(const QualType& QT) {
-  return QT.getLocalQualifiers() == Qualifiers{};
-}
-
 /// Unwrap reflected type for profiling
 static void profileTypeReflection(llvm::FoldingSetNodeID &ID, QualType QT) {
   // TODO(Reflection)
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 0bd243d126d13..7a20185400419 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1309,10 +1309,6 @@ void CXXNameMangler::mangleReflection(ReflectionKind Kind,
   //                  [ n ]                              # data member
   //                  description
 
-  auto  isTypeAliasAsReflectionName = [](const QualType& QT) {
-    return QT.getLocalQualifiers() == Qualifiers{};
-  };
-
   Out << "LDm";
   switch (Kind) {
   case ReflectionKind::Null:

>From 4425fb7484d693c043046fae4f4eb52b5e27b02a Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 18:11:44 -0400
Subject: [PATCH 11/17] move isTypeAliasAsReflectionName to inside
 CXXReflectExpr

---
 clang/include/clang/AST/ExprCXX.h    | 24 ++++++++++++++++++++++++
 clang/include/clang/AST/Reflection.h | 25 -------------------------
 clang/lib/AST/APValue.cpp            |  2 +-
 clang/lib/AST/ItaniumMangle.cpp      |  2 +-
 4 files changed, 26 insertions(+), 27 deletions(-)

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 4ed388877a2ab..7979bb79cf108 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5545,6 +5545,30 @@ class CXXReflectExpr : public Expr {
     return cast<TypeSourceInfo *>(Operand);
   }
 
+  /// [expr.reflect] p5, if a reflect-expression R matches the form
+  /// ^^reflection-name it is interpreted as such; the identifier is looked up
+  /// and the representation of R is determined as follows:
+  /// - if lookup finds a type alias A, R represents the type the underlying
+  ///   entity of A if A was introduced by the declaration of a template
+  ///   parameter; otherwise, R represents A.
+
+  /// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id,
+  /// if type-id is neither a placeholder type nor
+  /// in the form of nested-name-specifier_opt template_opt simple-template-id
+  /// then R represents the type denoted by the type-id
+
+  // In particular, this means that e.g. '^^const Alias' is reflection of
+  // a type, not an alias. For example:
+  //
+  // using foo = const int;
+  // ^^int       // Type
+  // ^^const int // Type
+  // ^^foo       // Alias
+  // ^^const foo // Type
+  static bool isTypeAliasAsReflectionName(QualType QT) {
+    return QT.getLocalQualifiers() == Qualifiers{};
+  }
+
   child_range children() {
     // TODO(Reflection)
     return child_range(child_iterator(), child_iterator());
diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h
index d77fb450c141f..732aefde80885 100644
--- a/clang/include/clang/AST/Reflection.h
+++ b/clang/include/clang/AST/Reflection.h
@@ -13,7 +13,6 @@
 #ifndef LLVM_CLANG_AST_REFLECTION_H
 #define LLVM_CLANG_AST_REFLECTION_H
 
-#include "clang/AST/Type.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang {
@@ -35,30 +34,6 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
   return OS;
 }
 
-/// [expr.reflect] p5, if a reflect-expression R matches the form
-/// ^^reflection-name it is interpreted as such; the identifier is looked up
-/// and the representation of R is determined as follows:
-/// - if lookup finds a type alias A, R represents the type the underlying
-///   entity of A if A was introduced by the declaration of a template
-///   parameter; otherwise, R represents A.
-
-/// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id,
-/// if type-id is neither a placeholder type nor
-/// in the form of nested-name-specifier_opt template_opt simple-template-id
-/// then R represents the type denoted by the type-id
-
-// In particular, this means that e.g. '^^const Alias' is reflection of
-// a type, not an alias. For example:
-//
-// using foo = const int;
-// ^^int       // Type
-// ^^const int // Type
-// ^^foo       // Alias
-// ^^const foo // Type
-inline bool isTypeAliasAsReflectionName(QualType QT) {
-  return QT.getLocalQualifiers() == Qualifiers{};
-}
-
 } // namespace clang
 
 #endif
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index 7fa843b89b4d4..cbbadcb63c704 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -497,7 +497,7 @@ static void profileIntValue(llvm::FoldingSetNodeID &ID, const llvm::APInt &V) {
 static void profileTypeReflection(llvm::FoldingSetNodeID &ID, QualType QT) {
   // TODO(Reflection)
 
-  if (isTypeAliasAsReflectionName(QT)) {
+  if (CXXReflectExpr::isTypeAliasAsReflectionName(QT)) {
     if (const auto *TDT = QT->getAs<TypedefType>()) {
       ID.AddBoolean(true);
       ID.AddPointer(TDT->getDecl()->getCanonicalDecl());
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 7a20185400419..a87ae921104e0 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1318,7 +1318,7 @@ void CXXNameMangler::mangleReflection(ReflectionKind Kind,
     const auto *TSI = static_cast<const TypeSourceInfo *>(OpaqueOperand);
     QualType QT = TSI->getType();
 
-    if (isTypeAliasAsReflectionName(QT)) {
+    if (CXXReflectExpr::isTypeAliasAsReflectionName(QT)) {
       if (const auto *TDT = QT->getAs<TypedefType>()) {
         Out << "ta";
         mangleName(TDT->getDecl()->getCanonicalDecl());

>From a63a4646f26f39b2f2c29f81148a3f0cf6cb1190 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 18:13:30 -0400
Subject: [PATCH 12/17] change variable name

---
 clang/lib/AST/ExprConstant.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 30086822583d8..c1393b619e7ff 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11251,12 +11251,12 @@ class ReflectionEvaluator : public ExprEvaluatorBase<ReflectionEvaluator> {
 bool ReflectionEvaluator::VisitCXXReflectExpr(const CXXReflectExpr *E) {
   switch (E->getKind()) {
   case ReflectionKind::Null: {
-    APValue Result(ReflectionKind::Null, /*Operand=*/nullptr);
-    return Success(Result, E);
+    APValue ReflectionValue(ReflectionKind::Null, /*Operand=*/nullptr);
+    return Success(ReflectionValue, E);
   }
   case ReflectionKind::Type: {
-    APValue Result(ReflectionKind::Type, E->getOpaqueValue());
-    return Success(Result, E);
+    APValue ReflectionValue(ReflectionKind::Type, E->getOpaqueValue());
+    return Success(ReflectionValue, E);
   }
   }
   llvm_unreachable("invalid reflection");

>From a1a476e25e8316bd88a451de95a39d4d09dafbfe Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 18:51:45 -0400
Subject: [PATCH 13/17] isTypeAliasAsReflectionName in Reflection.h

---
 clang/include/clang/AST/ExprCXX.h    | 24 ------------------------
 clang/include/clang/AST/Reflection.h | 25 +++++++++++++++++++++++++
 clang/lib/AST/APValue.cpp            |  2 +-
 clang/lib/AST/ItaniumMangle.cpp      |  2 +-
 4 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 7979bb79cf108..4ed388877a2ab 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5545,30 +5545,6 @@ class CXXReflectExpr : public Expr {
     return cast<TypeSourceInfo *>(Operand);
   }
 
-  /// [expr.reflect] p5, if a reflect-expression R matches the form
-  /// ^^reflection-name it is interpreted as such; the identifier is looked up
-  /// and the representation of R is determined as follows:
-  /// - if lookup finds a type alias A, R represents the type the underlying
-  ///   entity of A if A was introduced by the declaration of a template
-  ///   parameter; otherwise, R represents A.
-
-  /// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id,
-  /// if type-id is neither a placeholder type nor
-  /// in the form of nested-name-specifier_opt template_opt simple-template-id
-  /// then R represents the type denoted by the type-id
-
-  // In particular, this means that e.g. '^^const Alias' is reflection of
-  // a type, not an alias. For example:
-  //
-  // using foo = const int;
-  // ^^int       // Type
-  // ^^const int // Type
-  // ^^foo       // Alias
-  // ^^const foo // Type
-  static bool isTypeAliasAsReflectionName(QualType QT) {
-    return QT.getLocalQualifiers() == Qualifiers{};
-  }
-
   child_range children() {
     // TODO(Reflection)
     return child_range(child_iterator(), child_iterator());
diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h
index 732aefde80885..4b2710bc6a27f 100644
--- a/clang/include/clang/AST/Reflection.h
+++ b/clang/include/clang/AST/Reflection.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_AST_REFLECTION_H
 #define LLVM_CLANG_AST_REFLECTION_H
 
+#include "clang/AST/TypeBase.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang {
@@ -34,6 +35,30 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
   return OS;
 }
 
+/// [expr.reflect] p5, if a reflect-expression R matches the form
+/// ^^reflection-name it is interpreted as such; the identifier is looked up
+/// and the representation of R is determined as follows:
+/// - if lookup finds a type alias A, R represents the type the underlying
+///   entity of A if A was introduced by the declaration of a template
+///   parameter; otherwise, R represents A.
+
+/// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id,
+/// if type-id is neither a placeholder type nor
+/// in the form of nested-name-specifier_opt template_opt simple-template-id
+/// then R represents the type denoted by the type-id
+
+// In particular, this means that e.g. '^^const Alias' is reflection of
+// a type, not an alias. For example:
+//
+// using foo = const int;
+// ^^int       // Type
+// ^^const int // Type
+// ^^foo       // Alias
+// ^^const foo // Type
+static bool isTypeAliasAsReflectionName(QualType QT) {
+  return QT.getLocalQualifiers() == Qualifiers{};
+}
+
 } // namespace clang
 
 #endif
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index cbbadcb63c704..7fa843b89b4d4 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -497,7 +497,7 @@ static void profileIntValue(llvm::FoldingSetNodeID &ID, const llvm::APInt &V) {
 static void profileTypeReflection(llvm::FoldingSetNodeID &ID, QualType QT) {
   // TODO(Reflection)
 
-  if (CXXReflectExpr::isTypeAliasAsReflectionName(QT)) {
+  if (isTypeAliasAsReflectionName(QT)) {
     if (const auto *TDT = QT->getAs<TypedefType>()) {
       ID.AddBoolean(true);
       ID.AddPointer(TDT->getDecl()->getCanonicalDecl());
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index a87ae921104e0..7a20185400419 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1318,7 +1318,7 @@ void CXXNameMangler::mangleReflection(ReflectionKind Kind,
     const auto *TSI = static_cast<const TypeSourceInfo *>(OpaqueOperand);
     QualType QT = TSI->getType();
 
-    if (CXXReflectExpr::isTypeAliasAsReflectionName(QT)) {
+    if (isTypeAliasAsReflectionName(QT)) {
       if (const auto *TDT = QT->getAs<TypedefType>()) {
         Out << "ta";
         mangleName(TDT->getDecl()->getCanonicalDecl());

>From 874271084bc1b2c9930b6e7895b07f83764216ac Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 19:25:09 -0400
Subject: [PATCH 14/17] unused local var

---
 clang/lib/AST/ByteCode/Compiler.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index a698c35871d2e..ca285e89b2250 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -4528,7 +4528,6 @@ bool Compiler<Emitter>::VisitCXXReflectExpr(const CXXReflectExpr *E) {
 
   switch (E->getKind()) {
   case ReflectionKind::Type: {
-    APValue Result(ReflectionKind::Type, E->getOpaqueValue());
     return this->emitReflectValue(E->getKind(), E->getOpaqueValue(), E);
   }
   case ReflectionKind::Null:

>From edf9358e4146bbd05893b8f72a58d42a4de0b6ea Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 19:25:43 -0400
Subject: [PATCH 15/17] implement StmtProfiler for VisitCXXReflectExpr

---
 clang/lib/AST/StmtProfile.cpp | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index fdefb6d6b2e1f..fbc2b6de4001a 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2237,8 +2237,26 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) {
 }
 
 void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) {
-  // TODO(Reflection): Implement this.
-  assert(false && "not implemented yet");
+  VisitExpr(E);
+  ID.AddInteger(static_cast<int>(E->getKind()));
+  switch (E->getKind()) {
+  case ReflectionKind::Null:
+    break;
+  case ReflectionKind::Type: {
+    QualType QT = E->getTypeSourceInfo()->getType();
+    if (isTypeAliasAsReflectionName(QT)) {
+      if (const auto *TDT = QT->getAs<TypedefType>()) {
+        ID.AddBoolean(true);
+        VisitDecl(TDT->getDecl()->getCanonicalDecl());
+        break;
+      }
+    }
+    ID.AddBoolean(false);
+    VisitType(QT);
+    break;
+  }
+  }
+  assert(false && "unknown or unimplemented reflection entities");
 }
 
 void

>From 5677d1541a13c5c5a537906207c002252955e7e1 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 19:37:29 -0400
Subject: [PATCH 16/17] return stmt

---
 clang/lib/AST/StmtProfile.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index fbc2b6de4001a..c2531c49c5de3 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2241,19 +2241,19 @@ void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) {
   ID.AddInteger(static_cast<int>(E->getKind()));
   switch (E->getKind()) {
   case ReflectionKind::Null:
-    break;
+    return;
   case ReflectionKind::Type: {
     QualType QT = E->getTypeSourceInfo()->getType();
     if (isTypeAliasAsReflectionName(QT)) {
       if (const auto *TDT = QT->getAs<TypedefType>()) {
         ID.AddBoolean(true);
         VisitDecl(TDT->getDecl()->getCanonicalDecl());
-        break;
+        return;
       }
     }
     ID.AddBoolean(false);
     VisitType(QT);
-    break;
+    return;
   }
   }
   assert(false && "unknown or unimplemented reflection entities");

>From 59c1d4eaea27fd6466d465b495296bce26e3e196 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Mon, 27 Jul 2026 20:01:13 -0400
Subject: [PATCH 17/17] small fixes

---
 clang/include/clang/AST/Reflection.h |  2 +-
 clang/lib/AST/StmtProfile.cpp        | 22 ++--------------------
 2 files changed, 3 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h
index 4b2710bc6a27f..2deffae7f8112 100644
--- a/clang/include/clang/AST/Reflection.h
+++ b/clang/include/clang/AST/Reflection.h
@@ -55,7 +55,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 // ^^const int // Type
 // ^^foo       // Alias
 // ^^const foo // Type
-static bool isTypeAliasAsReflectionName(QualType QT) {
+inline bool isTypeAliasAsReflectionName(QualType QT) {
   return QT.getLocalQualifiers() == Qualifiers{};
 }
 
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index c2531c49c5de3..fdefb6d6b2e1f 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2237,26 +2237,8 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) {
 }
 
 void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) {
-  VisitExpr(E);
-  ID.AddInteger(static_cast<int>(E->getKind()));
-  switch (E->getKind()) {
-  case ReflectionKind::Null:
-    return;
-  case ReflectionKind::Type: {
-    QualType QT = E->getTypeSourceInfo()->getType();
-    if (isTypeAliasAsReflectionName(QT)) {
-      if (const auto *TDT = QT->getAs<TypedefType>()) {
-        ID.AddBoolean(true);
-        VisitDecl(TDT->getDecl()->getCanonicalDecl());
-        return;
-      }
-    }
-    ID.AddBoolean(false);
-    VisitType(QT);
-    return;
-  }
-  }
-  assert(false && "unknown or unimplemented reflection entities");
+  // TODO(Reflection): Implement this.
+  assert(false && "not implemented yet");
 }
 
 void



More information about the cfe-commits mailing list