[clang] [libcxx] [lldb] [clang]: implement std::meta::is_type (PR #211653)

Nhat Nguyen via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 23 12:53:00 PDT 2026


https://github.com/changkhothuychung created https://github.com/llvm/llvm-project/pull/211653

This PR implements `std::meta::is_type(info)` by introducing a builtin `__builtin_meta_is_type`.  It also includes two feature-test macros `__cpp_impl_reflection` and `__cpp_lib_reflection` introduced in [P2996 ](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2996r13.html). `__cpp_impl_reflection` is only enabled when `-freflection` is on, and `__cpp_lib_reflection` depends on `__cpp_impl_reflection`. 

Header `<meta>` is created in libc++. 

>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 1/2] 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 48133336caf74cd9dc0617983df1c9e7a7918a53 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Thu, 23 Jul 2026 15:44:27 -0400
Subject: [PATCH 2/2] implement std::meta::is_type

---
 clang/include/clang/Basic/Builtins.td         |  6 ++
 clang/lib/AST/ASTContext.cpp                  |  3 +
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      | 10 +++
 clang/lib/AST/ByteCode/Reflect.h              |  2 +
 clang/lib/AST/ExprConstant.cpp                |  7 ++
 clang/lib/Frontend/InitPreprocessor.cpp       |  4 ++
 clang/test/SemaCXX/builtin-meta-is-type.cpp   | 48 +++++++++++++
 clang/test/SemaCXX/consteval-builtin.cpp      | 18 +++++
 clang/utils/TableGen/ClangBuiltinsEmitter.cpp |  1 +
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/meta                           | 38 +++++++++++
 libcxx/include/module.modulemap.in            |  5 ++
 libcxx/include/version                        |  4 ++
 .../meta.version.compile.pass.cpp             | 68 +++++++++++++++++++
 .../version.version.compile.pass.cpp          | 33 +++++++++
 libcxx/test/std/meta/is_type.compile.pass.cpp | 51 ++++++++++++++
 .../generate_feature_test_macro_components.py |  7 ++
 18 files changed, 308 insertions(+)
 create mode 100644 clang/test/SemaCXX/builtin-meta-is-type.cpp
 create mode 100644 libcxx/include/meta
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/meta/is_type.compile.pass.cpp

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 344a712ddc585..244a791938093 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1207,6 +1207,12 @@ def IsWithinLifetime : LangBuiltin<"CXX_LANG"> {
   let Prototype = "bool(void*)";
 }
 
+def MetaIsType : LangBuiltin<"CXX_LANG"> {
+  let Spellings = ["__builtin_meta_is_type"];
+  let Attributes = [NoThrow, Consteval];
+  let Prototype = "bool(std::meta::info)";
+}
+
 def ClearPadding : Builtin {
   let Spellings = ["__builtin_clear_padding"];
   let Attributes = [NoThrow, CustomTypeChecking];
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c94598662915d..eef973690a350 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12835,6 +12835,9 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
   case 'M':
     Type = Context.getObjCSuperType();
     break;
+  case 'r':
+    Type = Context.MetaInfoTy;
+    break;
   case 'a':
     Type = Context.getBuiltinVaListType();
     assert(!Type.isNull() && "builtin va list type not initialized!");
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index a539fb26abc08..de48e2b84fed9 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2535,6 +2535,13 @@ static bool interp__builtin_object_size(InterpState &S, CodePtr OpPC,
   return false;
 }
 
+static bool interp__builtin_meta_is_type(InterpState &S, CodePtr OpPC,
+                                         const CallExpr *) {
+  const Reflect &R = S.Stk.pop<Reflect>();
+  S.Stk.push<Boolean>(Boolean::from(R.getKind() == ReflectionKind::Type));
+  return true;
+}
+
 static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC,
                                                const CallExpr *Call) {
 
@@ -5454,6 +5461,9 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
   case Builtin::BI__builtin_is_within_lifetime:
     return interp__builtin_is_within_lifetime(S, OpPC, Call);
 
+  case Builtin::BI__builtin_meta_is_type:
+    return interp__builtin_meta_is_type(S, OpPC, Call);
+
   case Builtin::BI__builtin_elementwise_add_sat:
     return interp__builtin_elementwise_int_binop(
         S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) {
diff --git a/clang/lib/AST/ByteCode/Reflect.h b/clang/lib/AST/ByteCode/Reflect.h
index dcd00a3bad782..f5b31743f5f7a 100644
--- a/clang/lib/AST/ByteCode/Reflect.h
+++ b/clang/lib/AST/ByteCode/Reflect.h
@@ -31,6 +31,8 @@ class Reflect final {
   Reflect(ReflectionKind Kind, const void *Operand)
       : Kind(Kind), Operand(Operand) {}
 
+  ReflectionKind getKind() const { return Kind; }
+
   ComparisonCategoryResult compare(const Reflect &RHS) const {
     llvm::FoldingSetNodeID LID, RID;
     APValue(Kind, Operand).Profile(LID);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 30086822583d8..90d7771f61885 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17252,6 +17252,13 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
       return Success(*result, E);
     return false;
 
+  case Builtin::BI__builtin_meta_is_type: {
+    APValue Reflection;
+    if (!EvaluateReflection(E->getArg(0), Reflection, Info))
+      return false;
+    return Success(Reflection.getReflectionOperandKind() == ReflectionKind::Type, E);
+  }
+
   case Builtin::BI__builtin_ctz:
   case Builtin::BI__builtin_ctzl:
   case Builtin::BI__builtin_ctzll:
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 8b6ff844d0daa..0266734132a73 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -766,6 +766,10 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
   Builder.defineMacro("__cpp_variadic_friend", "202403L");
   Builder.defineMacro("__cpp_trivial_relocatability", "202502L");
 
+  // C++26 reflection.
+  if (LangOpts.Reflection)
+    Builder.defineMacro("__cpp_impl_reflection", "202506L");
+
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
   Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");
diff --git a/clang/test/SemaCXX/builtin-meta-is-type.cpp b/clang/test/SemaCXX/builtin-meta-is-type.cpp
new file mode 100644
index 0000000000000..7060a2c06772a
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-meta-is-type.cpp
@@ -0,0 +1,48 @@
+// 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 S {};
+namespace A{};
+
+consteval void test_pass() {
+  static_assert(__builtin_meta_is_type(^^int));
+  static_assert(__builtin_meta_is_type(^^unsigned));
+  static_assert(__builtin_meta_is_type(^^long));
+  static_assert(__builtin_meta_is_type(^^long long));
+  static_assert(__builtin_meta_is_type(^^short));
+  static_assert(__builtin_meta_is_type(^^char));
+  static_assert(__builtin_meta_is_type(^^signed char));
+  static_assert(__builtin_meta_is_type(^^unsigned char));
+  static_assert(__builtin_meta_is_type(^^wchar_t));
+  static_assert(__builtin_meta_is_type(^^char8_t));
+  static_assert(__builtin_meta_is_type(^^char16_t));
+  static_assert(__builtin_meta_is_type(^^char32_t));
+  static_assert(__builtin_meta_is_type(^^bool));
+  static_assert(__builtin_meta_is_type(^^float));
+  static_assert(__builtin_meta_is_type(^^double));
+  static_assert(__builtin_meta_is_type(^^long double));
+  static_assert(__builtin_meta_is_type(^^void));
+  static_assert(__builtin_meta_is_type(^^decltype(nullptr)));
+
+  constexpr info null{};
+  static_assert(!__builtin_meta_is_type(null));
+}
+
+consteval void test_fail() {
+  static_assert(__builtin_meta_is_type(^^S)); // expected-error {{unknown or unimplemented reflectable entity}}
+  static_assert(!__builtin_meta_is_type(^^A)); // expected-error {{unknown or unimplemented reflectable entity}}
+}
+
+consteval bool test_with_no_arg() {
+  return __builtin_meta_is_type(); // expected-error {{too few arguments to function call, expected 1, have 0}}
+}
+
+consteval bool test_with_more_than_args_needed() {
+  return __builtin_meta_is_type(^^int, ^^float); // expected-error {{too many arguments to function call, expected 1, have 2}}
+}
+
+consteval bool test_with_wrong_type() {
+  return __builtin_meta_is_type(42); // expected-error {{cannot initialize a parameter of type 'std::meta::info' with an rvalue of type 'int'}}
+}
diff --git a/clang/test/SemaCXX/consteval-builtin.cpp b/clang/test/SemaCXX/consteval-builtin.cpp
index 3ba95b4dbd9b5..7b45e3ddc1f78 100644
--- a/clang/test/SemaCXX/consteval-builtin.cpp
+++ b/clang/test/SemaCXX/consteval-builtin.cpp
@@ -25,6 +25,24 @@
 // precxx20-error at -3 {{does not have the constexpr builtin}}
 // c-error at -4 {{does not have the constexpr builtin}}
 
+#if __has_builtin(__builtin_meta_is_type)
+#error has the builtin
+#else
+#error does not have the builtin
+#endif
+// cxx20-cxx26-error at -4 {{has the builtin}}
+// precxx20-error at -3 {{does not have the builtin}}
+// c-error at -4 {{does not have the builtin}}
+
+#if __has_constexpr_builtin(__builtin_meta_is_type)
+#error has the constexpr builtin
+#else
+#error does not have the constexpr builtin
+#endif
+// cxx20-cxx26-error at -4 {{has the constexpr builtin}}
+// precxx20-error at -3 {{does not have the constexpr builtin}}
+// c-error at -4 {{does not have the constexpr builtin}}
+
 #if __cplusplus < 201103L
 #define static_assert __extension__ _Static_assert
 #define CONSTEXPR11
diff --git a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
index 22c81522f9e41..259df9275641a 100644
--- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
+++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
@@ -376,6 +376,7 @@ class PrototypeParser {
                                .Case("short", "s")
                                .Case("sigjmp_buf", "SJ")
                                .Case("size_t", "z")
+                               .Case("std::meta::info", "r")
                                .Case("ucontext_t", "K")
                                .Case("uint8_t", "UBi")
                                .Case("uint16_t", "UTi")
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..4be0f3a2f10d2 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -522,6 +522,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_reference_wrapper``                            ``202403L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_reflection``                                   ``202506L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_saturation_arithmetic``                        ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_senders``                                      *unimplemented*
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..d76a92fda3aa5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1060,6 +1060,7 @@ set(files
   mdspan
   memory
   memory_resource
+  meta
   mutex
   new
   numbers
diff --git a/libcxx/include/meta b/libcxx/include/meta
new file mode 100644
index 0000000000000..b0954ea86d82f
--- /dev/null
+++ b/libcxx/include/meta
@@ -0,0 +1,38 @@
+// -*- 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 _LIBCPP_META
+#define _LIBCPP_META
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26 && defined(__cpp_impl_reflection)
+
+namespace meta {
+
+using info = decltype(^^int);
+
+#  if __has_builtin(__builtin_meta_is_type)
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI consteval bool is_type(info __r) {
+  return __builtin_meta_is_type(__r);
+}
+#  endif
+
+} // namespace meta
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_META
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..947e754271798 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1739,6 +1739,11 @@ module std {
     export *
   }
 
+  module meta {
+    header "meta"
+    export *
+  }
+
   module mutex {
     module lock_guard     { header "__mutex/lock_guard.h" }
     module mutex          { header "__mutex/mutex.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..31fafd4878f06 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -230,6 +230,7 @@ __cpp_lib_raw_memory_algorithms                         202411L <memory>
 __cpp_lib_rcu                                           202306L <rcu>
 __cpp_lib_reference_from_temporary                      202202L <type_traits>
 __cpp_lib_reference_wrapper                             202403L <functional>
+__cpp_lib_reflection                                    202506L <meta>
 __cpp_lib_remove_cvref                                  201711L <type_traits>
 __cpp_lib_result_of_sfinae                              201210L <functional> <type_traits>
 __cpp_lib_robust_nonmodifying_seq_ops                   201304L <algorithm>
@@ -626,6 +627,9 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_raw_memory_algorithms                202411L
 // # define __cpp_lib_rcu                                  202306L
 # define __cpp_lib_reference_wrapper                    202403L
+# if defined(__cpp_impl_reflection)
+#   define __cpp_lib_reflection                         202506L
+# endif
 # define __cpp_lib_saturation_arithmetic                202603L
 // # define __cpp_lib_senders                              202406L
 // # define __cpp_lib_smart_ptr_owner_equality             202306L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp
new file mode 100644
index 0000000000000..ffb3107747251
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <meta>
+
+// Test the feature test macros defined by <meta>
+
+// clang-format off
+
+#include <meta>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  if defined(__cpp_impl_reflection)
+#    ifndef __cpp_lib_reflection
+#      error "__cpp_lib_reflection should be defined in c++26"
+#    endif
+#    if __cpp_lib_reflection != 202506L
+#      error "__cpp_lib_reflection should have the value 202506L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_reflection
+#      error "__cpp_lib_reflection should not be defined when the requirement 'defined(__cpp_impl_reflection)' is not met!"
+#    endif
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index f2d74cc99d864..e7ecf2eea855c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -748,6 +748,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should not be defined before c++20"
 #  endif
@@ -1724,6 +1728,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should not be defined before c++20"
 #  endif
@@ -2868,6 +2876,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should not be defined before c++20"
 #  endif
@@ -4276,6 +4288,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should be defined in c++20"
 #  endif
@@ -5924,6 +5940,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should be defined in c++23"
 #  endif
@@ -7896,6 +7916,19 @@
 #    error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
 #  endif
 
+#  if defined(__cpp_impl_reflection)
+#    ifndef __cpp_lib_reflection
+#      error "__cpp_lib_reflection should be defined in c++26"
+#    endif
+#    if __cpp_lib_reflection != 202506L
+#      error "__cpp_lib_reflection should have the value 202506L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_reflection
+#      error "__cpp_lib_reflection should not be defined when the requirement 'defined(__cpp_impl_reflection)' is not met!"
+#    endif
+#  endif
+
 #  ifndef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/meta/is_type.compile.pass.cpp b/libcxx/test/std/meta/is_type.compile.pass.cpp
new file mode 100644
index 0000000000000..9cfec64b7d1cc
--- /dev/null
+++ b/libcxx/test/std/meta/is_type.compile.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+// ADDITIONAL_COMPILE_FLAGS(clang): -Xclang -freflection
+
+
+// consteval bool is_type(info);
+
+#include <meta>
+
+#include "test_macros.h"
+
+#if defined(__cpp_impl_reflection) && __has_builtin(__builtin_meta_is_type)
+
+ASSERT_SAME_TYPE(decltype(std::meta::is_type(std::meta::info{})), bool);
+
+template <class T>
+concept is_type_callable = requires(T t) { std::meta::is_type(t); };
+
+static_assert(is_type_callable<std::meta::info>);
+static_assert(!is_type_callable<int>);
+static_assert(!is_type_callable<void*>);
+static_assert(!is_type_callable<decltype(nullptr)>);
+
+static_assert(std::meta::is_type(^^int));
+static_assert(std::meta::is_type(^^unsigned));
+static_assert(std::meta::is_type(^^long));
+static_assert(std::meta::is_type(^^long long));
+static_assert(std::meta::is_type(^^short));
+static_assert(std::meta::is_type(^^char));
+static_assert(std::meta::is_type(^^signed char));
+static_assert(std::meta::is_type(^^unsigned char));
+static_assert(std::meta::is_type(^^wchar_t));
+static_assert(std::meta::is_type(^^char8_t));
+static_assert(std::meta::is_type(^^char16_t));
+static_assert(std::meta::is_type(^^char32_t));
+static_assert(std::meta::is_type(^^bool));
+static_assert(std::meta::is_type(^^float));
+static_assert(std::meta::is_type(^^double));
+static_assert(std::meta::is_type(^^long double));
+static_assert(std::meta::is_type(^^void));
+static_assert(std::meta::is_type(^^decltype(nullptr)));
+
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..485f51881341c 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1231,6 +1231,13 @@ def add_version_header(tc):
             "values": {"c++26": 202403}, # P2944R3: Comparisons for reference_wrapper
             "headers": ["functional"],
         },
+        {
+            "name": "__cpp_lib_reflection",
+            "values": {"c++26": 202506}, # P2996R13: Reflection for C++26
+            "headers": ["meta"],
+            "test_suite_guard": "defined(__cpp_impl_reflection)",
+            "libcxx_guard": "defined(__cpp_impl_reflection)",
+        },
         {
             "name": "__cpp_lib_remove_cvref",
             "values": {"c++20": 201711},



More information about the cfe-commits mailing list