[clang] [clang] Implement the __builtin_type_order intrinsic for P2830R10 Constexpr Type Ordering (PR #216462)

Matthias Wippich via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 18 20:08:57 PDT 2026


https://github.com/Tsche updated https://github.com/llvm/llvm-project/pull/216462

>From c6ee0d5a900f11bcc7f0f81a13f867518928a4df Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Sat, 15 Aug 2026 09:02:00 +0200
Subject: [PATCH 1/8] [clang] Implement the __builtin_type_order intrinsic for
 P2830R10 Constexpr Type Ordering

---
 clang/docs/LanguageExtensions.md              |  3 +
 clang/docs/ReleaseNotes.md                    |  3 +
 clang/include/clang/AST/ASTNodeTraverser.h    |  5 ++
 clang/include/clang/AST/ComputeDependence.h   |  2 +
 clang/include/clang/AST/ExprCXX.h             | 43 ++++++++++
 clang/include/clang/AST/RecursiveASTVisitor.h |  5 ++
 clang/include/clang/AST/StmtDataCollectors.td |  6 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +-
 clang/include/clang/Basic/StmtNodes.td        |  1 +
 clang/include/clang/Basic/TokenKinds.def      |  1 +
 clang/include/clang/Parse/Parser.h            |  3 +
 clang/include/clang/Sema/Sema.h               |  9 ++
 .../include/clang/Serialization/ASTBitCodes.h |  1 +
 clang/lib/AST/ASTImporter.cpp                 | 19 ++++-
 clang/lib/AST/ASTStructuralEquivalence.cpp    |  8 ++
 clang/lib/AST/ComputeDependence.cpp           |  8 ++
 clang/lib/AST/Expr.cpp                        |  1 +
 clang/lib/AST/ExprClassification.cpp          |  1 +
 clang/lib/AST/ExprConstant.cpp                |  1 +
 clang/lib/AST/ItaniumMangle.cpp               |  1 +
 clang/lib/AST/StmtPrinter.cpp                 |  8 ++
 clang/lib/AST/StmtProfile.cpp                 |  6 ++
 clang/lib/Parse/ParseExpr.cpp                 |  4 +
 clang/lib/Parse/ParseExprCXX.cpp              | 33 ++++++++
 clang/lib/Sema/SemaExceptionSpec.cpp          |  1 +
 clang/lib/Sema/SemaTypeTraits.cpp             | 66 +++++++++++++++
 clang/lib/Sema/TreeTransform.h                | 35 +++++++-
 clang/lib/Serialization/ASTReaderStmt.cpp     | 13 +++
 clang/lib/Serialization/ASTWriter.cpp         |  1 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |  8 ++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |  1 +
 clang/test/AST/builtin-type-order.cpp         | 35 ++++++++
 clang/test/PCH/builtin-type-order.cpp         | 31 +++++++
 clang/test/Parser/builtin_type_order.cpp      | 14 ++++
 clang/test/SemaCXX/builtin-type-order.cpp     | 82 +++++++++++++++++++
 clang/tools/libclang/CIndex.cpp               |  6 ++
 clang/tools/libclang/CXCursor.cpp             |  1 +
 37 files changed, 464 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/AST/builtin-type-order.cpp
 create mode 100644 clang/test/PCH/builtin-type-order.cpp
 create mode 100644 clang/test/Parser/builtin_type_order.cpp
 create mode 100644 clang/test/SemaCXX/builtin-type-order.cpp

diff --git a/clang/docs/LanguageExtensions.md b/clang/docs/LanguageExtensions.md
index 85586174e9190..6080f70ec8311 100644
--- a/clang/docs/LanguageExtensions.md
+++ b/clang/docs/LanguageExtensions.md
@@ -2058,6 +2058,9 @@ The following type trait primitives are supported by Clang. Those traits marked
 - `__builtin_lt_synthesizes_from_spaceship`, `__builtin_gt_synthesizes_from_spaceship`,
   `__builtin_le_synthesizes_from_spaceship`, `__builtin_ge_synthesizes_from_spaceship` (Clang):
   These builtins can be used to determine whether the corresponding operator is synthesized from a spaceship operator.
+- `__builtin_type_order` (C++): Returns `std::strong_ordering::less` if `T` precedes `U` in an
+  implementation-defined total ordering of all types, `std::strong_ordering::greater` if `U` precedes `T`, 
+  and `std::strong_ordering::equal` if they are the same type.
 
 In addition, the following expression traits are supported:
 
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 9a19bb2f2d5c7..6ac9e3678ade1 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -125,6 +125,9 @@ features cannot lower the translation-unit ABI level;
 
 #### C++2c Feature Support
 
+- Added `__builtin_type_order` for compatibility with GCC as part of the
+  implementation of [P2830R10](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2830r10.html) (Constexpr Type Ordering).
+
 - Clang now supports [P3533R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3533r2.html) (constexpr virtual inheritance).
 
 #### C++23 Feature Support
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index a8a73c5b72d33..14fcbf3656752 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -934,6 +934,11 @@ class ASTNodeTraverser
       Visit(A->getType());
   }
 
+  void VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *E) {
+    Visit(E->getLhsType());
+    Visit(E->getRhsType());
+  }
+
   void VisitLambdaExpr(const LambdaExpr *Node) {
     if (Traversal == TK_IgnoreUnlessSpelledInSource) {
       for (unsigned I = 0, N = Node->capture_size(); I != N; ++I) {
diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h
index 3a3c86842501a..c78cf273dc17e 100644
--- a/clang/include/clang/AST/ComputeDependence.h
+++ b/clang/include/clang/AST/ComputeDependence.h
@@ -83,6 +83,7 @@ class CXXDependentScopeMemberExpr;
 class MaterializeTemporaryExpr;
 class CXXFoldExpr;
 class CXXParenListInitExpr;
+class BuiltinTypeOrderExpr;
 class TypeTraitExpr;
 class ConceptSpecializationExpr;
 class SYCLUniqueStableNameExpr;
@@ -178,6 +179,7 @@ ExprDependence computeDependence(CXXDependentScopeMemberExpr *E);
 ExprDependence computeDependence(MaterializeTemporaryExpr *E);
 ExprDependence computeDependence(CXXFoldExpr *E);
 ExprDependence computeDependence(CXXParenListInitExpr *E);
+ExprDependence computeDependence(BuiltinTypeOrderExpr *E);
 ExprDependence computeDependence(TypeTraitExpr *E);
 ExprDependence computeDependence(ConceptSpecializationExpr *E,
                                  bool ValueDependent);
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index d3d3b9c6d6326..9b56fa5515f7f 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2886,6 +2886,49 @@ class CXXPseudoDestructorExpr : public Expr {
   }
 };
 
+/// Represents a C++26 __builtin_type_order(T, U) expression. Used to implement
+/// std::type_order.
+class BuiltinTypeOrderExpr final : public Expr {
+  friend class ASTStmtReader;
+
+  SourceLocation Loc;
+  SourceLocation RParenLoc;
+  TypeSourceInfo *Lhs;
+  TypeSourceInfo *Rhs;
+
+public:
+  BuiltinTypeOrderExpr(QualType T, SourceLocation Loc, TypeSourceInfo *LHS,
+                       TypeSourceInfo *RHS, SourceLocation RParenLoc)
+      : Expr(BuiltinTypeOrderExprClass, T, VK_PRValue, OK_Ordinary), Loc(Loc),
+        RParenLoc(RParenLoc), Lhs(LHS), Rhs(RHS) {
+    setDependence(computeDependence(this));
+  }
+
+  explicit BuiltinTypeOrderExpr(EmptyShell Empty)
+      : Expr(BuiltinTypeOrderExprClass, Empty) {}
+
+  TypeSourceInfo *getLhsTypeInfo() const { return Lhs; }
+  TypeSourceInfo *getRhsTypeInfo() const { return Rhs; }
+
+  QualType getLhsType() const { return getLhsTypeInfo()->getType(); }
+  QualType getRhsType() const { return getRhsTypeInfo()->getType(); }
+
+  SourceLocation getBeginLoc() const LLVM_READONLY { return Loc; }
+  SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == BuiltinTypeOrderExprClass;
+  }
+
+  child_range children() {
+    return child_range(child_iterator(), child_iterator());
+  }
+
+  const_child_range children() const {
+    return const_child_range(const_child_iterator(), const_child_iterator());
+  }
+};
+
 /// A type trait used in the implementation of various C++11 and
 /// Library TR1 trait templates.
 ///
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 617990b82edca..2ee34d4675865 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2865,6 +2865,11 @@ DEF_TRAVERSE_STMT(TypeTraitExpr, {
     TRY_TO(TraverseTypeLoc(S->getArg(I)->getTypeLoc()));
 })
 
+DEF_TRAVERSE_STMT(BuiltinTypeOrderExpr, {
+  TRY_TO(TraverseTypeLoc(S->getLhsTypeInfo()->getTypeLoc()));
+  TRY_TO(TraverseTypeLoc(S->getRhsTypeInfo()->getTypeLoc()));
+})
+
 DEF_TRAVERSE_STMT(ArrayTypeTraitExpr, {
   TRY_TO(TraverseTypeLoc(S->getQueriedTypeSourceInfo()->getTypeLoc()));
 })
diff --git a/clang/include/clang/AST/StmtDataCollectors.td b/clang/include/clang/AST/StmtDataCollectors.td
index abf4b5f34d349..8b8c9da988579 100644
--- a/clang/include/clang/AST/StmtDataCollectors.td
+++ b/clang/include/clang/AST/StmtDataCollectors.td
@@ -37,6 +37,12 @@ class TypeTraitExpr {
       addData(S->getArg(i)->getType());
   }];
 }
+class BuiltinTypeOrderExpr {
+  code Code = [{
+    addData(S->getLhsType());
+    addData(S->getRhsType());
+  }];
+}
 
 //--- Calls --------------------------------------------------------------//
 class CallExpr {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b314c17ad27bd..9356416eed302 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13354,7 +13354,7 @@ def warn_invalid_default_version_priority
 
 // three-way comparison operator diagnostics
 def err_implied_comparison_category_type_not_found : Error<
-  "cannot %select{use builtin operator '<=>'|default 'operator<=>'}1 "
+  "cannot %select{use builtin operator '<=>'|default 'operator<=>'|retrieve type order}1 "
   "because type '%0' was not found; include <compare>">;
 def err_spaceship_argument_narrowing : Error<
   "argument to 'operator<=>' "
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index 6df244d7a8c90..0c0f82f944ceb 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -150,6 +150,7 @@ def CXXStdInitializerListExpr : StmtNode<Expr>;
 def CXXNewExpr : StmtNode<Expr>;
 def CXXDeleteExpr : StmtNode<Expr>;
 def CXXPseudoDestructorExpr : StmtNode<Expr>;
+def BuiltinTypeOrderExpr : StmtNode<Expr>;
 def TypeTraitExpr : StmtNode<Expr>;
 def ArrayTypeTraitExpr : StmtNode<Expr>;
 def ExpressionTraitExpr : StmtNode<Expr>;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index dc9c7d8109467..38995aca639a5 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -469,6 +469,7 @@ KEYWORD(__builtin_FUNCSIG           , KEYMS)
 KEYWORD(__builtin_LINE              , KEYALL)
 KEYWORD(__builtin_COLUMN            , KEYALL)
 KEYWORD(__builtin_source_location   , KEYCXX)
+KEYWORD(__builtin_type_order        , KEYCXX)
 
 KEYWORD(__builtin_va_arg            , KEYALL)
 KEYWORD(__extension__               , KEYALL)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 163aa483a84e3..feb39ecb5ca55 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5195,6 +5195,9 @@ class Parser : public CodeCompletionHandler {
   ///
   ExprResult ParseTypeTrait();
 
+  /// Parse __builtin_type_order(T, U), used to implement C++26 std::type_order.
+  ExprResult ParseBuiltinTypeOrder();
+
   //===--------------------------------------------------------------------===//
   // Embarcadero: Arary and Expression Traits
 
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d931e70cb2342..ecccc1d985924 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5335,6 +5335,8 @@ class Sema final : public SemaBase {
     /// typically only applies to 'std::strong_ordering', due to the implicit
     /// fallback return value.
     DefaultedOperator,
+    /// The '__builtin_type_order' builtin needed 'std::strong_ordering'.
+    BuiltinTypeOrder,
   };
 
   /// Lookup the specified comparison category types in the standard
@@ -8723,6 +8725,13 @@ class Sema final : public SemaBase {
 
   bool CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N);
 
+  ExprResult ActOnBuiltinTypeOrder(SourceLocation KWLoc, ParsedType LhsTy,
+                                   ParsedType RhsTy, SourceLocation RParenLoc);
+  ExprResult BuildBuiltinTypeOrderExpr(SourceLocation KWLoc,
+                                       TypeSourceInfo *LhsT,
+                                       TypeSourceInfo *RhsT,
+                                       SourceLocation RParenLoc);
+
   /// Parsed one of the type trait support pseudo-functions.
   ExprResult ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
                             ArrayRef<ParsedType> Args,
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index b582cbdadc070..191918df5e5e7 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1938,6 +1938,7 @@ enum StmtCode {
 
   EXPR_OPAQUE_VALUE,                // OpaqueValueExpr
   EXPR_BINARY_CONDITIONAL_OPERATOR, // BinaryConditionalOperator
+  EXPR_BUILTIN_TYPE_ORDER,          // BuiltinTypeOrderExpr
   EXPR_TYPE_TRAIT,                  // TypeTraitExpr
   EXPR_ARRAY_TYPE_TRAIT,            // ArrayTypeTraitIntExpr
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 5be9ce780aec5..a8c03b1eb768c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -696,7 +696,9 @@ namespace clang {
     ExpectedStmt VisitArrayInitIndexExpr(ArrayInitIndexExpr *E);
     ExpectedStmt VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E);
     ExpectedStmt VisitCXXNamedCastExpr(CXXNamedCastExpr *E);
-    ExpectedStmt VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *E);
+    ExpectedStmt
+    VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *E);
+    ExpectedStmt VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E);
     ExpectedStmt VisitTypeTraitExpr(TypeTraitExpr *E);
     ExpectedStmt VisitCXXTypeidExpr(CXXTypeidExpr *E);
     ExpectedStmt VisitCXXFoldExpr(CXXFoldExpr *E);
@@ -9157,6 +9159,21 @@ ExpectedStmt ASTNodeImporter::VisitSubstNonTypeTemplateParmExpr(
       ToParamType, E->getIndex(), E->getPackIndex(), E->getFinal());
 }
 
+ExpectedStmt
+ASTNodeImporter::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
+  Error Err = Error::success();
+  auto ToType = importChecked(Err, E->getType());
+  auto ToBeginLoc = importChecked(Err, E->getBeginLoc());
+  auto ToEndLoc = importChecked(Err, E->getEndLoc());
+  auto *ToLhs = importChecked(Err, E->getLhsTypeInfo());
+  auto *ToRhs = importChecked(Err, E->getRhsTypeInfo());
+  if (Err)
+    return std::move(Err);
+
+  return new (Importer.getToContext())
+      BuiltinTypeOrderExpr(ToType, ToBeginLoc, ToLhs, ToRhs, ToEndLoc);
+}
+
 ExpectedStmt ASTNodeImporter::VisitTypeTraitExpr(TypeTraitExpr *E) {
   Error Err = Error::success();
   auto ToType = importChecked(Err, E->getType());
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index d8bbfbe5dac72..7cbcdc1bf8495 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -351,6 +351,14 @@ class StmtComparer {
     return true;
   }
 
+  bool IsStmtEquivalent(const BuiltinTypeOrderExpr *E1,
+                        const BuiltinTypeOrderExpr *E2) {
+    return IsStructurallyEquivalent(Context, E1->getLhsType(),
+                                    E2->getLhsType()) &&
+           IsStructurallyEquivalent(Context, E1->getRhsType(),
+                                    E2->getRhsType());
+  }
+
   bool IsStmtEquivalent(const CXXDependentScopeMemberExpr *E1,
                         const CXXDependentScopeMemberExpr *E2) {
     if (!IsStructurallyEquivalent(Context, E1->getMember(), E2->getMember())) {
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 7e6bd69711c5b..40ab05f7f8ebc 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -931,6 +931,14 @@ ExprDependence clang::computeDependence(TypeTraitExpr *E) {
   return D;
 }
 
+ExprDependence clang::computeDependence(BuiltinTypeOrderExpr *E) {
+  ExprDependence D = ExprDependence::None;
+  for (TypeSourceInfo *Arg : {E->getLhsTypeInfo(), E->getRhsTypeInfo()})
+    D |= toExprDependenceAsWritten(Arg->getType()->getDependence()) &
+         ~ExprDependence::Type;
+  return D;
+}
+
 ExprDependence clang::computeDependence(ConceptSpecializationExpr *E,
                                         bool ValueDependent) {
   auto TA = TemplateArgumentDependence::None;
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 5d7ee4710481c..55a5d8214e5da 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3747,6 +3747,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
   case CXXNullPtrLiteralExprClass:
   case CXXThisExprClass:
   case CXXScalarValueInitExprClass:
+  case BuiltinTypeOrderExprClass:
   case TypeTraitExprClass:
   case ArrayTypeTraitExprClass:
   case ExpressionTraitExprClass:
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index ef071cdef66b6..37d799e329df0 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -192,6 +192,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
   case Expr::FloatingLiteralClass:
   case Expr::CXXNoexceptExprClass:
   case Expr::CXXScalarValueInitExprClass:
+  case Expr::BuiltinTypeOrderExprClass:
   case Expr::TypeTraitExprClass:
   case Expr::ArrayTypeTraitExprClass:
   case Expr::ExpressionTraitExprClass:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 480d5119a5363..47df253ddfef4 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -22441,6 +22441,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
   case Expr::ObjCBoolLiteralExprClass:
   case Expr::CXXBoolLiteralExprClass:
   case Expr::CXXScalarValueInitExprClass:
+  case Expr::BuiltinTypeOrderExprClass:
   case Expr::TypeTraitExprClass:
   case Expr::ConceptSpecializationExprClass:
   case Expr::RequiresExprClass:
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index f6c4ca1ae6ba8..126c1b67452c5 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5056,6 +5056,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   case Expr::StmtExprClass:
   case Expr::ArrayTypeTraitExprClass:
   case Expr::ExpressionTraitExprClass:
+  case Expr::BuiltinTypeOrderExprClass:
   case Expr::VAArgExprClass:
   case Expr::CUDAKernelCallExprClass:
   case Expr::AsTypeExprClass:
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index eeb377c794e05..5f220b26936b1 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2708,6 +2708,14 @@ void StmtPrinter::VisitTypeTraitExpr(TypeTraitExpr *E) {
   OS << ")";
 }
 
+void StmtPrinter::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
+  OS << "__builtin_type_order(";
+  E->getLhsType().print(OS, Policy);
+  OS << ", ";
+  E->getRhsType().print(OS, Policy);
+  OS << ')';
+}
+
 void StmtPrinter::VisitArrayTypeTraitExpr(ArrayTypeTraitExpr *E) {
   OS << getTraitSpelling(E->getTrait()) << '(';
   E->getQueriedType().print(OS, Policy);
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 45c962aa27553..7baf7443b02f6 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2320,6 +2320,12 @@ void StmtProfiler::VisitTypeTraitExpr(const TypeTraitExpr *S) {
     VisitType(S->getArg(I)->getType());
 }
 
+void StmtProfiler::VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *S) {
+  VisitExpr(S);
+  VisitType(S->getLhsType());
+  VisitType(S->getRhsType());
+}
+
 void StmtProfiler::VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *S) {
   VisitExpr(S);
   ID.AddInteger(S->getTrait());
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 87cd7a01451cf..a84de3a72170c 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1523,6 +1523,10 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand,
     break;
   }
 
+  case tok::kw___builtin_type_order:
+    Res = ParseBuiltinTypeOrder();
+    break;
+
 #define TYPE_TRAIT(N,Spelling,K) \
   case tok::kw_##Spelling:
 #include "clang/Basic/TokenKinds.def"
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f9a0dcc7d53af..57e4f2096a9d9 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -3490,6 +3490,39 @@ static ExpressionTrait ExpressionTraitFromTokKind(tok::TokenKind kind) {
   }
 }
 
+ExprResult Parser::ParseBuiltinTypeOrder() {
+  SourceLocation Loc = ConsumeToken();
+
+  BalancedDelimiterTracker Parens(*this, tok::l_paren);
+  if (Parens.expectAndConsume())
+    return ExprError();
+
+  TypeResult LHS = ParseTypeName(/*SourceRange=*/nullptr,
+                                 DeclaratorContext::TemplateTypeArg);
+  if (LHS.isInvalid()) {
+    Parens.skipToEnd();
+    return ExprError();
+  }
+
+  if (ExpectAndConsume(tok::comma)) {
+    Parens.skipToEnd();
+    return ExprError();
+  }
+
+  TypeResult RHS = ParseTypeName(/*SourceRange=*/nullptr,
+                                 DeclaratorContext::TemplateTypeArg);
+  if (RHS.isInvalid()) {
+    Parens.skipToEnd();
+    return ExprError();
+  }
+
+  if (Parens.consumeClose())
+    return ExprError();
+
+  return Actions.ActOnBuiltinTypeOrder(Loc, LHS.get(), RHS.get(),
+                                       Parens.getCloseLocation());
+}
+
 ExprResult Parser::ParseTypeTrait() {
   tok::TokenKind Kind = Tok.getKind();
 
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index d41137ddc85d5..213aa6701fdb0 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1390,6 +1390,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::AddrLabelExprClass:
   case Expr::ArrayTypeTraitExprClass:
   case Expr::AtomicExprClass:
+  case Expr::BuiltinTypeOrderExprClass:
   case Expr::TypeTraitExprClass:
   case Expr::CXXBoolLiteralExprClass:
   case Expr::CXXNoexceptExprClass:
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 53ab235f3654a..78e6d8d76b114 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -10,7 +10,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/Mangle.h"
 #include "clang/AST/TemplateBase.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/BuiltinTraits.h"
@@ -25,6 +27,9 @@
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaHLSL.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
 
 using namespace clang;
 
@@ -1463,6 +1468,67 @@ bool Sema::CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N) {
   return true;
 }
 
+static ComparisonCategoryResult EvaluateTypeOrder(Sema &S, QualType LHS,
+                                                  QualType RHS) {
+  if (S.Context.hasSameType(LHS, RHS))
+    return ComparisonCategoryResult::Equal;
+
+  std::unique_ptr<MangleContext> MC(S.Context.createMangleContext());
+  SmallString<64> LhsName, RhsName;
+  {
+    llvm::raw_svector_ostream LhsOut(LhsName), RhsOut(RhsName);
+    MC->mangleCanonicalTypeName(LHS, LhsOut);
+    MC->mangleCanonicalTypeName(RHS, RhsOut);
+  }
+
+  int Result = LhsName.compare(RhsName);
+  return Result == 0  ? ComparisonCategoryResult::Equal
+         : Result > 0 ? ComparisonCategoryResult::Greater
+                      : ComparisonCategoryResult::Less;
+}
+
+ExprResult Sema::ActOnBuiltinTypeOrder(SourceLocation KWLoc, ParsedType LhsTy,
+                                       ParsedType RhsTy,
+                                       SourceLocation RParenLoc) {
+  SmallVector<TypeSourceInfo *, 2> Args;
+  for (auto ArgT : {LhsTy, RhsTy}) {
+    TypeSourceInfo *TInfo;
+    QualType T = GetTypeFromParser(ArgT, &TInfo);
+    if (!TInfo)
+      TInfo = Context.getTrivialTypeSourceInfo(T, KWLoc);
+
+    Args.push_back(TInfo);
+  }
+  return BuildBuiltinTypeOrderExpr(KWLoc, Args[0], Args[1], RParenLoc);
+}
+
+ExprResult Sema::BuildBuiltinTypeOrderExpr(SourceLocation KWLoc,
+                                           TypeSourceInfo *LhsT,
+                                           TypeSourceInfo *RhsT,
+                                           SourceLocation RParenLoc) {
+  QualType StrongOrdering =
+      CheckComparisonCategoryType(ComparisonCategoryType::StrongOrdering, KWLoc,
+                                  ComparisonCategoryUsage::BuiltinTypeOrder);
+  if (StrongOrdering.isNull())
+    return ExprError();
+
+  QualType LHS = LhsT->getType();
+  QualType RHS = RhsT->getType();
+  if (LHS->isDependentType() || RHS->isDependentType())
+    return new (Context)
+        BuiltinTypeOrderExpr(StrongOrdering, KWLoc, LhsT, RhsT, RParenLoc);
+
+  ComparisonCategoryResult Result = EvaluateTypeOrder(*this, LHS, RHS);
+  VarDecl *ResultVD = Context.CompCategories.getInfoForType(StrongOrdering)
+                          .getValueInfo(Result)
+                          ->VD;
+  Expr *ResultExpr =
+      BuildDeclRefExpr(ResultVD, ResultVD->getType(), VK_LValue, KWLoc);
+  return PerformCopyInitialization(
+      InitializedEntity::InitializeTemporary(StrongOrdering), KWLoc,
+      ResultExpr);
+}
+
 enum class TypeTraitReturnType {
   Bool,
   SizeT,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e6abb3ad577c6..035a76c1612de 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3567,6 +3567,17 @@ class TreeTransform {
                                     Operand);
   }
 
+  /// Build a new type ordering expression.
+  ///
+  /// By default, performs semantic analysis to build the new expression.
+  /// Subclasses may override this routine to provide different behavior.
+  ExprResult RebuildBuiltinTypeOrderExpr(SourceLocation StartLoc,
+                                         TypeSourceInfo *LhsT,
+                                         TypeSourceInfo *RhsT,
+                                         SourceLocation RParenLoc) {
+    return getSema().BuildBuiltinTypeOrderExpr(StartLoc, LhsT, RhsT, RParenLoc);
+  }
+
   /// Build a new type trait expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -15485,9 +15496,29 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr(UnresolvedLookupExpr *Old,
                                             Old->requiresADL(), &TransArgs);
 }
 
-template<typename Derived>
+template <typename Derived>
 ExprResult
-TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
+TreeTransform<Derived>::TransformBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
+  SmallVector<TypeSourceInfo *, 2> Args;
+  bool ArgChanged = false;
+  for (TypeSourceInfo *From : {E->getLhsTypeInfo(), E->getRhsTypeInfo()}) {
+    TypeSourceInfo *To = getDerived().TransformType(From);
+    if (!To)
+      return ExprError();
+
+    Args.push_back(To);
+    ArgChanged |= To != From;
+  }
+
+  if (!getDerived().AlwaysRebuild() && !ArgChanged)
+    return E;
+
+  return getDerived().RebuildBuiltinTypeOrderExpr(E->getBeginLoc(), Args[0],
+                                                  Args[1], E->getEndLoc());
+}
+
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
   bool ArgChanged = false;
   SmallVector<TypeSourceInfo *, 4> Args;
   for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I) {
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 1afde95b05290..2b53f447a29af 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2225,6 +2225,15 @@ void ASTStmtReader::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
   E->NamingClass = readDeclAs<CXXRecordDecl>();
 }
 
+void ASTStmtReader::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
+  VisitExpr(E);
+  SourceRange Range = readSourceRange();
+  E->Loc = Range.getBegin();
+  E->RParenLoc = Range.getEnd();
+  E->Lhs = readTypeSourceInfo();
+  E->Rhs = readTypeSourceInfo();
+}
+
 void ASTStmtReader::VisitTypeTraitExpr(TypeTraitExpr *E) {
   VisitExpr(E);
   E->TypeTraitExprBits.IsBooleanTypeTrait = Record.readInt();
@@ -4452,6 +4461,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
     }
 
+    case EXPR_BUILTIN_TYPE_ORDER:
+      S = new (Context) BuiltinTypeOrderExpr(Empty);
+      break;
+
     case EXPR_TYPE_TRAIT:
       S = TypeTraitExpr::CreateDeserialized(
           Context, Record[ASTStmtReader::NumExprFields],
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index f34b2ff182bc8..1c7df49705658 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -866,6 +866,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream,
   RECORD(EXPR_CXX_NOEXCEPT);
   RECORD(EXPR_OPAQUE_VALUE);
   RECORD(EXPR_BINARY_CONDITIONAL_OPERATOR);
+  RECORD(EXPR_BUILTIN_TYPE_ORDER);
   RECORD(EXPR_TYPE_TRAIT);
   RECORD(EXPR_ARRAY_TYPE_TRAIT);
   RECORD(EXPR_PACK_EXPANSION);
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 3854eabd62a46..780359f58ed34 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2244,6 +2244,14 @@ void ASTStmtWriter::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
   }
 }
 
+void ASTStmtWriter::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
+  VisitExpr(E);
+  Record.AddSourceRange(E->getSourceRange());
+  Record.AddTypeSourceInfo(E->getLhsTypeInfo());
+  Record.AddTypeSourceInfo(E->getRhsTypeInfo());
+  Code = serialization::EXPR_BUILTIN_TYPE_ORDER;
+}
+
 void ASTStmtWriter::VisitTypeTraitExpr(TypeTraitExpr *E) {
   VisitExpr(E);
   Record.push_back(E->TypeTraitExprBits.IsBooleanTypeTrait);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 328ed5b23dd83..64ff7766ba9a4 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1699,6 +1699,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::DependentScopeDeclRefExprClass:
     case Stmt::ArrayTypeTraitExprClass:
     case Stmt::ExpressionTraitExprClass:
+    case Stmt::BuiltinTypeOrderExprClass:
     case Stmt::UnresolvedLookupExprClass:
     case Stmt::UnresolvedMemberExprClass:
     case Stmt::RecoveryExprClass:
diff --git a/clang/test/AST/builtin-type-order.cpp b/clang/test/AST/builtin-type-order.cpp
new file mode 100644
index 0000000000000..7b2447e647273
--- /dev/null
+++ b/clang/test/AST/builtin-type-order.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -ast-print %s | FileCheck %s --check-prefix=PRINT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -ast-dump -ast-dump-filter=dependent_order %s | FileCheck %s --check-prefix=AST
+
+namespace std {
+enum class __order : signed char { less = -1, equal = 0, greater = 1 };
+
+struct strong_ordering {
+  __order value;
+
+  constexpr explicit strong_ordering(__order value) : value(value) {}
+
+  static const strong_ordering less;
+  static const strong_ordering equal;
+  static const strong_ordering greater;
+};
+
+inline constexpr strong_ordering strong_ordering::less(__order::less);
+inline constexpr strong_ordering strong_ordering::equal(__order::equal);
+inline constexpr strong_ordering strong_ordering::greater(__order::greater);
+} // namespace std
+
+template <class T, class U>
+constexpr std::strong_ordering dependent_order() {
+  return __builtin_type_order(T, U);
+}
+
+// PRINT: template <class T, class U> constexpr std::strong_ordering dependent_order()
+// PRINT: return __builtin_type_order(T, U);
+
+// AST-LABEL: FunctionTemplateDecl {{.*}} dependent_order
+// AST: BuiltinTypeOrderExpr {{.*}} 'std::strong_ordering'
+// AST-NEXT: |-TemplateTypeParmType {{.*}} 'T' dependent
+// AST-NEXT: | `-TemplateTypeParm {{.*}} 'T'
+// AST-NEXT: `-TemplateTypeParmType {{.*}} 'U' dependent
+// AST-NEXT:   `-TemplateTypeParm {{.*}} 'U'
diff --git a/clang/test/PCH/builtin-type-order.cpp b/clang/test/PCH/builtin-type-order.cpp
new file mode 100644
index 0000000000000..f948028bb494f
--- /dev/null
+++ b/clang/test/PCH/builtin-type-order.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -std=c++20 -emit-pch -o %t %s
+// RUN: %clang_cc1 -std=c++20 -include-pch %t -DTEST %s -verify
+// RUN: %clang_cc1 -std=c++20 -include-pch %t -DTEST %s -verify -fexperimental-new-constant-interpreter
+
+#ifndef TEST
+namespace std {
+enum class __order : signed char { less = -1, equal = 0, greater = 1 };
+
+struct strong_ordering {
+  __order value;
+
+  constexpr explicit strong_ordering(__order value) : value(value) {}
+
+  static const strong_ordering less;
+  static const strong_ordering equal;
+  static const strong_ordering greater;
+};
+
+inline constexpr strong_ordering strong_ordering::less(__order::less);
+inline constexpr strong_ordering strong_ordering::equal(__order::equal);
+inline constexpr strong_ordering strong_ordering::greater(__order::greater);
+} // namespace std
+
+template <class T, class U>
+constexpr auto type_order = __builtin_type_order(T, U);
+#else
+static_assert(type_order<int, int>.value == std::__order::equal);
+static_assert(type_order<int, long>.value != std::__order::equal);
+
+// expected-no-diagnostics
+#endif
diff --git a/clang/test/Parser/builtin_type_order.cpp b/clang/test/Parser/builtin_type_order.cpp
new file mode 100644
index 0000000000000..293a6b930681b
--- /dev/null
+++ b/clang/test/Parser/builtin_type_order.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+template <typename... T>
+void syntax_errors() {
+  (void)__builtin_type_order(int); // expected-error {{expected ','}}
+  (void)__builtin_type_order(T...); // expected-error {{expected ','}}
+
+  (void)__builtin_type_order(int, long, char); 
+  // expected-error at -1 {{expected ')'}} \
+  //   expected-note at -1 {{to match this '('}}
+  (void)__builtin_type_order(int, T...);
+  // expected-error at -1 {{expected ')'}} \
+  //   expected-note at -1 {{to match this '('}}
+}
diff --git a/clang/test/SemaCXX/builtin-type-order.cpp b/clang/test/SemaCXX/builtin-type-order.cpp
new file mode 100644
index 0000000000000..2de69008b9e23
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-type-order.cpp
@@ -0,0 +1,82 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fsyntax-only -verify -DITANIUM %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fsyntax-only -verify -DITANIUM -fexperimental-new-constant-interpreter %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -std=c++20 -fsyntax-only -verify -DMICROSOFT %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -std=c++20 -fsyntax-only -verify -DMICROSOFT -fexperimental-new-constant-interpreter %s
+
+constexpr auto missing = __builtin_type_order(int, long);
+// expected-error at -1 {{cannot retrieve type order because type 'std::strong_ordering' was not found; include <compare>}}
+
+namespace std {
+  struct strong_ordering {
+  enum __order { LT = -1, EQ = 0, GT = 1 };
+  __order value;
+
+  constexpr explicit strong_ordering(__order value) : value(value) {}
+  constexpr bool operator==(strong_ordering const& other) const {
+    return value == other.value;
+  }
+  static const strong_ordering less;
+  static const strong_ordering equal;
+  static const strong_ordering greater;
+};
+
+inline constexpr strong_ordering strong_ordering::less(__order::LT);
+inline constexpr strong_ordering strong_ordering::equal(__order::EQ);
+inline constexpr strong_ordering strong_ordering::greater(__order::GT);
+} // namespace std
+
+static_assert(__is_same(decltype(__builtin_type_order(int, long)),
+                        std::strong_ordering));
+
+static_assert(__builtin_type_order(int, int) == std::strong_ordering::equal);
+static_assert(__builtin_type_order(int, int const) != std::strong_ordering::equal);
+static_assert(__builtin_type_order(void(*)(int), void(*)(int)) == std::strong_ordering::equal);
+
+static_assert(__builtin_type_order(void*, void const volatile*) != std::strong_ordering::equal);
+static_assert(__builtin_type_order(int, long) != std::strong_ordering::equal);
+static_assert(__builtin_type_order(int, long).value ==
+              -__builtin_type_order(long, int).value);
+
+using int_alias = int;
+static_assert(__builtin_type_order(int, int_alias) ==
+              std::strong_ordering::equal);
+
+struct incomplete;
+static_assert(__builtin_type_order(incomplete, incomplete) ==
+              std::strong_ordering::equal);
+static_assert(__builtin_type_order(int incomplete::*, char incomplete::*) != std::strong_ordering::equal);
+static_assert(__builtin_type_order(void (incomplete::*)(), void (incomplete::*)() const) != std::strong_ordering::equal);
+
+struct A {};
+struct B {};
+static_assert(__builtin_type_order(A, B) == std::strong_ordering::less);
+static_assert(__builtin_type_order(B, A) == std::strong_ordering::greater);
+static_assert(__builtin_type_order(void(*)(A), void(*)(B)) == std::strong_ordering::less);
+static_assert(__builtin_type_order(A[1], B[1]) == std::strong_ordering::less);
+
+#ifdef ITANIUM
+static_assert(__builtin_type_order(A, int) == std::strong_ordering::less);
+#endif
+#ifdef MICROSOFT
+static_assert(__builtin_type_order(A, int) == std::strong_ordering::greater);
+#endif
+
+template <class T> struct C;
+static_assert(__builtin_type_order(C<A>, C<B>) == std::strong_ordering::less);
+static_assert(__builtin_type_order(C<B>, C<A>) == std::strong_ordering::greater);
+
+
+template <class T, class U>
+constexpr std::strong_ordering dependent_order() {
+  return __builtin_type_order(T, U);
+}
+static_assert(dependent_order<int, int>() == std::strong_ordering::equal);
+static_assert(dependent_order<const int, const int>() ==
+              std::strong_ordering::equal);
+
+template <class T>
+constexpr bool dependent_constant_expression() {
+  static_assert(__builtin_type_order(T, T) == std::strong_ordering::equal);
+  return true;
+}
+static_assert(dependent_constant_expression<int>());
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index e485e6e2e5547..e798e691374b1 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -2128,6 +2128,7 @@ class EnqueueVisitor : public ConstStmtVisitor<EnqueueVisitor, void>,
   void VisitStmt(const Stmt *S);
   void VisitSwitchStmt(const SwitchStmt *S);
   void VisitWhileStmt(const WhileStmt *W);
+  void VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *E);
   void VisitTypeTraitExpr(const TypeTraitExpr *E);
   void VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E);
   void VisitExpressionTraitExpr(const ExpressionTraitExpr *E);
@@ -3271,6 +3272,11 @@ void EnqueueVisitor::VisitTypeTraitExpr(const TypeTraitExpr *E) {
     AddTypeLoc(E->getArg(I - 1));
 }
 
+void EnqueueVisitor::VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *E) {
+  AddTypeLoc(E->getRhsTypeInfo());
+  AddTypeLoc(E->getLhsTypeInfo());
+}
+
 void EnqueueVisitor::VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {
   AddTypeLoc(E->getQueriedTypeSourceInfo());
 }
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index e27964d0ad6e1..421630c764ebc 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -304,6 +304,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
   case Stmt::AsTypeExprClass:
   case Stmt::AtomicExprClass:
   case Stmt::BinaryConditionalOperatorClass:
+  case Stmt::BuiltinTypeOrderExprClass:
   case Stmt::TypeTraitExprClass:
   case Stmt::CoawaitExprClass:
   case Stmt::DependentCoawaitExprClass:

>From 6272eb44e77795475dee3225c95728f3c0597f7e Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Mon, 17 Aug 2026 02:12:07 +0200
Subject: [PATCH 2/8] rename Lhs/Rhs to LHS/RHS, break up nested conditional
 expression

---
 clang/include/clang/AST/ASTNodeTraverser.h    |  4 ++--
 clang/include/clang/AST/ExprCXX.h             | 14 +++++++-------
 clang/include/clang/AST/RecursiveASTVisitor.h |  4 ++--
 clang/include/clang/AST/StmtDataCollectors.td |  4 ++--
 clang/lib/AST/ASTImporter.cpp                 |  4 ++--
 clang/lib/AST/ASTStructuralEquivalence.cpp    |  8 ++++----
 clang/lib/AST/ComputeDependence.cpp           |  2 +-
 clang/lib/AST/StmtPrinter.cpp                 |  4 ++--
 clang/lib/AST/StmtProfile.cpp                 |  4 ++--
 clang/lib/Sema/SemaTypeTraits.cpp             |  7 ++++---
 clang/lib/Sema/TreeTransform.h                |  2 +-
 clang/lib/Serialization/ASTReaderStmt.cpp     |  4 ++--
 clang/lib/Serialization/ASTWriterStmt.cpp     |  4 ++--
 clang/tools/libclang/CIndex.cpp               |  4 ++--
 14 files changed, 35 insertions(+), 34 deletions(-)

diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index 14fcbf3656752..e7c02bdae2d74 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -935,8 +935,8 @@ class ASTNodeTraverser
   }
 
   void VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *E) {
-    Visit(E->getLhsType());
-    Visit(E->getRhsType());
+    Visit(E->getLHSType());
+    Visit(E->getRHSType());
   }
 
   void VisitLambdaExpr(const LambdaExpr *Node) {
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 9b56fa5515f7f..a8ad7065e60bb 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2893,25 +2893,25 @@ class BuiltinTypeOrderExpr final : public Expr {
 
   SourceLocation Loc;
   SourceLocation RParenLoc;
-  TypeSourceInfo *Lhs;
-  TypeSourceInfo *Rhs;
+  TypeSourceInfo *LHS;
+  TypeSourceInfo *RHS;
 
 public:
   BuiltinTypeOrderExpr(QualType T, SourceLocation Loc, TypeSourceInfo *LHS,
                        TypeSourceInfo *RHS, SourceLocation RParenLoc)
       : Expr(BuiltinTypeOrderExprClass, T, VK_PRValue, OK_Ordinary), Loc(Loc),
-        RParenLoc(RParenLoc), Lhs(LHS), Rhs(RHS) {
+        RParenLoc(RParenLoc), LHS(LHS), RHS(RHS) {
     setDependence(computeDependence(this));
   }
 
   explicit BuiltinTypeOrderExpr(EmptyShell Empty)
       : Expr(BuiltinTypeOrderExprClass, Empty) {}
 
-  TypeSourceInfo *getLhsTypeInfo() const { return Lhs; }
-  TypeSourceInfo *getRhsTypeInfo() const { return Rhs; }
+  TypeSourceInfo *getLHSTypeInfo() const { return LHS; }
+  TypeSourceInfo *getRHSTypeInfo() const { return RHS; }
 
-  QualType getLhsType() const { return getLhsTypeInfo()->getType(); }
-  QualType getRhsType() const { return getRhsTypeInfo()->getType(); }
+  QualType getLHSType() const { return getLHSTypeInfo()->getType(); }
+  QualType getRHSType() const { return getRHSTypeInfo()->getType(); }
 
   SourceLocation getBeginLoc() const LLVM_READONLY { return Loc; }
   SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; }
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 2ee34d4675865..e204da9ca3899 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2866,8 +2866,8 @@ DEF_TRAVERSE_STMT(TypeTraitExpr, {
 })
 
 DEF_TRAVERSE_STMT(BuiltinTypeOrderExpr, {
-  TRY_TO(TraverseTypeLoc(S->getLhsTypeInfo()->getTypeLoc()));
-  TRY_TO(TraverseTypeLoc(S->getRhsTypeInfo()->getTypeLoc()));
+  TRY_TO(TraverseTypeLoc(S->getLHSTypeInfo()->getTypeLoc()));
+  TRY_TO(TraverseTypeLoc(S->getRHSTypeInfo()->getTypeLoc()));
 })
 
 DEF_TRAVERSE_STMT(ArrayTypeTraitExpr, {
diff --git a/clang/include/clang/AST/StmtDataCollectors.td b/clang/include/clang/AST/StmtDataCollectors.td
index 8b8c9da988579..7863cca48d079 100644
--- a/clang/include/clang/AST/StmtDataCollectors.td
+++ b/clang/include/clang/AST/StmtDataCollectors.td
@@ -39,8 +39,8 @@ class TypeTraitExpr {
 }
 class BuiltinTypeOrderExpr {
   code Code = [{
-    addData(S->getLhsType());
-    addData(S->getRhsType());
+    addData(S->getLHSType());
+    addData(S->getRHSType());
   }];
 }
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index a8c03b1eb768c..e85f0c49e8807 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -9165,8 +9165,8 @@ ASTNodeImporter::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
   auto ToType = importChecked(Err, E->getType());
   auto ToBeginLoc = importChecked(Err, E->getBeginLoc());
   auto ToEndLoc = importChecked(Err, E->getEndLoc());
-  auto *ToLhs = importChecked(Err, E->getLhsTypeInfo());
-  auto *ToRhs = importChecked(Err, E->getRhsTypeInfo());
+  auto *ToLhs = importChecked(Err, E->getLHSTypeInfo());
+  auto *ToRhs = importChecked(Err, E->getRHSTypeInfo());
   if (Err)
     return std::move(Err);
 
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 7cbcdc1bf8495..e9292792bc823 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -353,10 +353,10 @@ class StmtComparer {
 
   bool IsStmtEquivalent(const BuiltinTypeOrderExpr *E1,
                         const BuiltinTypeOrderExpr *E2) {
-    return IsStructurallyEquivalent(Context, E1->getLhsType(),
-                                    E2->getLhsType()) &&
-           IsStructurallyEquivalent(Context, E1->getRhsType(),
-                                    E2->getRhsType());
+    return IsStructurallyEquivalent(Context, E1->getLHSType(),
+                                    E2->getLHSType()) &&
+           IsStructurallyEquivalent(Context, E1->getRHSType(),
+                                    E2->getRHSType());
   }
 
   bool IsStmtEquivalent(const CXXDependentScopeMemberExpr *E1,
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 40ab05f7f8ebc..8e14387641030 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -933,7 +933,7 @@ ExprDependence clang::computeDependence(TypeTraitExpr *E) {
 
 ExprDependence clang::computeDependence(BuiltinTypeOrderExpr *E) {
   ExprDependence D = ExprDependence::None;
-  for (TypeSourceInfo *Arg : {E->getLhsTypeInfo(), E->getRhsTypeInfo()})
+  for (TypeSourceInfo *Arg : {E->getLHSTypeInfo(), E->getRHSTypeInfo()})
     D |= toExprDependenceAsWritten(Arg->getType()->getDependence()) &
          ~ExprDependence::Type;
   return D;
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 5f220b26936b1..44acfc28113c4 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2710,9 +2710,9 @@ void StmtPrinter::VisitTypeTraitExpr(TypeTraitExpr *E) {
 
 void StmtPrinter::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
   OS << "__builtin_type_order(";
-  E->getLhsType().print(OS, Policy);
+  E->getLHSType().print(OS, Policy);
   OS << ", ";
-  E->getRhsType().print(OS, Policy);
+  E->getRHSType().print(OS, Policy);
   OS << ')';
 }
 
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 7baf7443b02f6..d2ff5f6742321 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2322,8 +2322,8 @@ void StmtProfiler::VisitTypeTraitExpr(const TypeTraitExpr *S) {
 
 void StmtProfiler::VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *S) {
   VisitExpr(S);
-  VisitType(S->getLhsType());
-  VisitType(S->getRhsType());
+  VisitType(S->getLHSType());
+  VisitType(S->getRHSType());
 }
 
 void StmtProfiler::VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *S) {
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 78e6d8d76b114..898e7ce71a311 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1482,9 +1482,10 @@ static ComparisonCategoryResult EvaluateTypeOrder(Sema &S, QualType LHS,
   }
 
   int Result = LhsName.compare(RhsName);
-  return Result == 0  ? ComparisonCategoryResult::Equal
-         : Result > 0 ? ComparisonCategoryResult::Greater
-                      : ComparisonCategoryResult::Less;
+  if (Result == 0)
+    return ComparisonCategoryResult::Equal;
+  return Result > 0 ? ComparisonCategoryResult::Greater
+                    : ComparisonCategoryResult::Less;
 }
 
 ExprResult Sema::ActOnBuiltinTypeOrder(SourceLocation KWLoc, ParsedType LhsTy,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 035a76c1612de..aa630a55fa965 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -15501,7 +15501,7 @@ ExprResult
 TreeTransform<Derived>::TransformBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
   SmallVector<TypeSourceInfo *, 2> Args;
   bool ArgChanged = false;
-  for (TypeSourceInfo *From : {E->getLhsTypeInfo(), E->getRhsTypeInfo()}) {
+  for (TypeSourceInfo *From : {E->getLHSTypeInfo(), E->getRHSTypeInfo()}) {
     TypeSourceInfo *To = getDerived().TransformType(From);
     if (!To)
       return ExprError();
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 2b53f447a29af..09572ffef827b 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2230,8 +2230,8 @@ void ASTStmtReader::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
   SourceRange Range = readSourceRange();
   E->Loc = Range.getBegin();
   E->RParenLoc = Range.getEnd();
-  E->Lhs = readTypeSourceInfo();
-  E->Rhs = readTypeSourceInfo();
+  E->LHS = readTypeSourceInfo();
+  E->RHS = readTypeSourceInfo();
 }
 
 void ASTStmtReader::VisitTypeTraitExpr(TypeTraitExpr *E) {
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 780359f58ed34..1983e7fef73a9 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2247,8 +2247,8 @@ void ASTStmtWriter::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
 void ASTStmtWriter::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
   VisitExpr(E);
   Record.AddSourceRange(E->getSourceRange());
-  Record.AddTypeSourceInfo(E->getLhsTypeInfo());
-  Record.AddTypeSourceInfo(E->getRhsTypeInfo());
+  Record.AddTypeSourceInfo(E->getLHSTypeInfo());
+  Record.AddTypeSourceInfo(E->getRHSTypeInfo());
   Code = serialization::EXPR_BUILTIN_TYPE_ORDER;
 }
 
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index e798e691374b1..e424bf412adc8 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -3273,8 +3273,8 @@ void EnqueueVisitor::VisitTypeTraitExpr(const TypeTraitExpr *E) {
 }
 
 void EnqueueVisitor::VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *E) {
-  AddTypeLoc(E->getRhsTypeInfo());
-  AddTypeLoc(E->getLhsTypeInfo());
+  AddTypeLoc(E->getRHSTypeInfo());
+  AddTypeLoc(E->getLHSTypeInfo());
 }
 
 void EnqueueVisitor::VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {

>From f9a0d890ad3f1a471f88d9977c91adedc69f6d95 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Wed, 19 Aug 2026 04:06:04 +0200
Subject: [PATCH 3/8] reimplement in terms of TypeTraitExpr

---
 clang/include/clang/AST/ExprCXX.h             | 11 ++-
 clang/include/clang/Basic/BuiltinTraits.td    |  5 ++
 clang/include/clang/Sema/Sema.h               |  4 +-
 clang/lib/AST/ByteCode/Compiler.cpp           | 16 ++++
 clang/lib/AST/ExprCXX.cpp                     | 30 ++++++--
 clang/lib/AST/ExprConstant.cpp                | 43 ++++++++---
 clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 43 ++++++++---
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |  1 +
 clang/lib/CodeGen/CGExprAgg.cpp               | 43 ++++++++---
 clang/lib/CodeGen/CGExprScalar.cpp            |  1 +
 clang/lib/Sema/SemaTypeTraits.cpp             | 74 +++++++++++++------
 clang/lib/StaticAnalyzer/Core/SValBuilder.cpp |  2 +
 clang/test/AST/ast-dump-traits.cpp            | 57 +++++++++++---
 clang/test/CIR/CodeGen/cxx-traits.cpp         | 61 +++++++++++++++
 clang/test/PCH/cxx-traits.cpp                 |  4 +
 clang/test/PCH/cxx-traits.h                   | 23 ++++++
 16 files changed, 343 insertions(+), 75 deletions(-)

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a8ad7065e60bb..2704df16acb87 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_AST_EXPRCXX_H
 
 #include "clang/AST/ASTConcept.h"
+#include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/ComputeDependence.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
@@ -2948,7 +2949,7 @@ class TypeTraitExpr final
 
   TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
                 ArrayRef<TypeSourceInfo *> Args, SourceLocation RParenLoc,
-                std::variant<bool, APValue> Value);
+                std::variant<bool, APValue, ComparisonCategoryResult> Value);
 
   TypeTraitExpr(EmptyShell Empty, bool IsStoredAsBool);
 
@@ -2977,6 +2978,12 @@ class TypeTraitExpr final
                                ArrayRef<TypeSourceInfo *> Args,
                                SourceLocation RParenLoc, APValue Value);
 
+  static TypeTraitExpr *Create(const ASTContext &C, QualType T,
+                               SourceLocation Loc, TypeTrait Kind,
+                               ArrayRef<TypeSourceInfo *> Args,
+                               SourceLocation RParenLoc,
+                               ComparisonCategoryResult Value);
+
   static TypeTraitExpr *CreateDeserialized(const ASTContext &C,
                                            bool IsStoredAsBool,
                                            unsigned NumArgs);
@@ -2996,7 +3003,7 @@ class TypeTraitExpr final
   }
 
   const APValue &getAPValue() const {
-    assert(!isValueDependent() && !TypeTraitExprBits.IsBooleanTypeTrait);
+    assert(!TypeTraitExprBits.IsBooleanTypeTrait);
     return *getTrailingObjects<APValue>();
   }
 
diff --git a/clang/include/clang/Basic/BuiltinTraits.td b/clang/include/clang/Basic/BuiltinTraits.td
index f5546acdb17c4..17a8b4a4623d1 100644
--- a/clang/include/clang/Basic/BuiltinTraits.td
+++ b/clang/include/clang/Basic/BuiltinTraits.td
@@ -126,6 +126,11 @@ def TypeCompatible : BinaryTrait {
   let KeyFlags = [KEYNOCXX];
 }
 
+def TypeOrder : BinaryTrait {
+  let Spelling = "__builtin_type_order2";
+  let KeyFlags = [KEYCXX];
+}
+
 // MS Extensions
 def IsInterfaceClass : UnaryTrait {
   let Spelling = "__is_interface_class";
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ecccc1d985924..4b41f3acdfb6e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5335,8 +5335,8 @@ class Sema final : public SemaBase {
     /// typically only applies to 'std::strong_ordering', due to the implicit
     /// fallback return value.
     DefaultedOperator,
-    /// The '__builtin_type_order' builtin needed 'std::strong_ordering'.
-    BuiltinTypeOrder,
+    /// A builtin needed 'std::strong_ordering' (eg. '__builtin_type_order').
+    Builtin,
   };
 
   /// Lookup the specified comparison category types in the standard
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index bfb5df69b0e3a..02dcb68026571 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3681,6 +3681,22 @@ bool Compiler<Emitter>::VisitTypeTraitExpr(const TypeTraitExpr *E) {
       return this->emitConstBool(E->getBoolValue(), E);
     return this->emitConst(E->getBoolValue(), E);
   }
+  if (!E->getType()->isIntegerType()) {
+    const ComparisonCategoryInfo &CmpInfo =
+        Ctx.getASTContext().CompCategories.getInfoForType(E->getType());
+    const auto Result =
+        ComparisonCategoryResult(E->getAPValue().getInt().getZExtValue());
+    const Record *R = getRecord(E->getType());
+    if (!R || R->getNumFields() == 0)
+      return false;
+    const Record::Field *Field = R->getField(0U);
+    PrimType FieldT = classifyPrim(Field->Decl->getType());
+    if (!this->emitConst(CmpInfo.getValueInfo(Result)->getIntValue(), FieldT,
+                         E))
+      return false;
+    return this->emitInitField(FieldT, Field->Offset, E);
+  }
+
   PrimType T = classifyPrim(E->getType());
   return this->visitAPValue(E->getAPValue(), T, E);
 }
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 6c1cde6540d85..13db61573bd7f 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -13,6 +13,7 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Attr.h"
+#include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/ComputeDependence.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclAccessPair.h"
@@ -1864,10 +1865,10 @@ bool MaterializeTemporaryExpr::isUsableInConstantExpressions(
          VD->isUsableInConstantExpressions(Context);
 }
 
-TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
-                             ArrayRef<TypeSourceInfo *> Args,
-                             SourceLocation RParenLoc,
-                             std::variant<bool, APValue> Value)
+TypeTraitExpr::TypeTraitExpr(
+    QualType T, SourceLocation Loc, TypeTrait Kind,
+    ArrayRef<TypeSourceInfo *> Args, SourceLocation RParenLoc,
+    std::variant<bool, APValue, ComparisonCategoryResult> Value)
     : Expr(TypeTraitExprClass, T, VK_PRValue, OK_Ordinary), Loc(Loc),
       RParenLoc(RParenLoc) {
   assert(Kind <= TT_Last && "invalid enum value!");
@@ -1879,9 +1880,14 @@ TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
   TypeTraitExprBits.IsBooleanTypeTrait = std::holds_alternative<bool>(Value);
   if (TypeTraitExprBits.IsBooleanTypeTrait)
     TypeTraitExprBits.Value = std::get<bool>(Value);
-  else
-    ::new (getTrailingObjects<APValue>())
-        APValue(std::get<APValue>(std::move(Value)));
+  else {
+    if (auto *CCR = std::get_if<ComparisonCategoryResult>(&Value)) {
+      llvm::APSInt EncodedValue = llvm::APSInt::get(llvm::to_underlying(*CCR));
+      ::new (getTrailingObjects<APValue>()) APValue(std::move(EncodedValue));
+    } else
+      ::new (getTrailingObjects<APValue>())
+          APValue(std::get<APValue>(std::move(Value)));
+  }
 
   TypeTraitExprBits.NumArgs = Args.size();
   assert(Args.size() == TypeTraitExprBits.NumArgs &&
@@ -1923,6 +1929,16 @@ TypeTraitExpr *TypeTraitExpr::Create(const ASTContext &C, QualType T,
   return new (Mem) TypeTraitExpr(T, Loc, Kind, Args, RParenLoc, Value);
 }
 
+TypeTraitExpr *TypeTraitExpr::Create(const ASTContext &C, QualType T,
+                                     SourceLocation Loc, TypeTrait Kind,
+                                     ArrayRef<TypeSourceInfo *> Args,
+                                     SourceLocation RParenLoc,
+                                     ComparisonCategoryResult Value) {
+  void *Mem =
+      C.Allocate(totalSizeToAlloc<APValue, TypeSourceInfo *>(1, Args.size()));
+  return new (Mem) TypeTraitExpr(T, Loc, Kind, Args, RParenLoc, Value);
+}
+
 TypeTraitExpr *TypeTraitExpr::CreateDeserialized(const ASTContext &C,
                                                  bool IsStoredAsBool,
                                                  unsigned NumArgs) {
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 47df253ddfef4..2053ae3beb1ec 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -42,6 +42,7 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/CharUnits.h"
+#include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/CurrentSourceLocExprScope.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/InferAlloc.h"
@@ -11357,6 +11358,7 @@ namespace {
     bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
     bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
     bool VisitBinCmp(const BinaryOperator *E);
+    bool VisitTypeTraitExpr(const TypeTraitExpr *E);
     bool VisitCXXParenListInitExpr(const CXXParenListInitExpr *E);
     bool VisitCXXParenListOrInitListExpr(const Expr *ExprToVisit,
                                          ArrayRef<Expr *> Args);
@@ -19443,6 +19445,22 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
   return DoAfter();
 }
 
+static bool EvaluateComparisonResult(EvalInfo &Info, const Expr *E,
+                                     ComparisonCategoryResult CCR,
+                                     APValue &Result) {
+  const ComparisonCategoryInfo &CmpInfo =
+      Info.Ctx.CompCategories.getInfoForType(E->getType());
+  const VarDecl *VD = CmpInfo.getValueInfo(CmpInfo.makeWeakResult(CCR))->VD;
+
+  // Check and evaluate the result as a constant expression.
+  LValue LV;
+  LV.set(VD);
+  if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result))
+    return false;
+  return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result,
+                                 ConstantExprKind::Normal);
+}
+
 bool RecordExprEvaluator::VisitBinCmp(const BinaryOperator *E) {
   if (!CheckLiteralType(Info, E))
     return false;
@@ -19465,24 +19483,25 @@ bool RecordExprEvaluator::VisitBinCmp(const BinaryOperator *E) {
       CCR = ComparisonCategoryResult::Unordered;
       break;
     }
-    // Evaluation succeeded. Lookup the information for the comparison category
-    // type and fetch the VarDecl for the result.
-    const ComparisonCategoryInfo &CmpInfo =
-        Info.Ctx.CompCategories.getInfoForType(E->getType());
-    const VarDecl *VD = CmpInfo.getValueInfo(CmpInfo.makeWeakResult(CCR))->VD;
-    // Check and evaluate the result as a constant expression.
-    LValue LV;
-    LV.set(VD);
-    if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result))
-      return false;
-    return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result,
-                                   ConstantExprKind::Normal);
+    return EvaluateComparisonResult(Info, E, CCR, Result);
   };
   return EvaluateComparisonBinaryOperator(Info, E, OnSuccess, [&]() {
     return ExprEvaluatorBaseTy::VisitBinCmp(E);
   });
 }
 
+bool RecordExprEvaluator::VisitTypeTraitExpr(const TypeTraitExpr *E) {
+  if (!CheckLiteralType(Info, E))
+    return false;
+
+  assert(!E->isStoredAsBoolean() && E->getAPValue().isInt() &&
+         "expected a non-boolean type trait with a stored value");
+
+  ComparisonCategoryResult CCR = static_cast<ComparisonCategoryResult>(
+      E->getAPValue().getInt().getZExtValue());
+  return EvaluateComparisonResult(Info, E, CCR, Result);
+}
+
 bool RecordExprEvaluator::VisitCXXParenListInitExpr(
     const CXXParenListInitExpr *E) {
   return VisitCXXParenListOrInitListExpr(E, E->getInitExprs());
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 6f57551c3ce24..dbc9eee8c5e6e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -147,6 +147,10 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
 
   void emitNullInitializationToLValue(mlir::Location loc, LValue lv);
 
+  void emitComparisonResult(const Expr *e, mlir::Location loc,
+                            const ComparisonCategoryInfo &cmpInfo,
+                            mlir::Value resultValue);
+
   void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); }
 
   void VisitArraySubscriptExpr(ArraySubscriptExpr *e) {
@@ -423,18 +427,22 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
       }
     }
 
-    // Create the return value in the destination slot.
-    ensureDest(loc, e->getType());
-    LValue destLVal = cgf.makeAddrLValue(dest.getAddress(), e->getType());
+    emitComparisonResult(e, loc, cmpInfo, resultScalar);
+  }
+
+  void VisitTypeTraitExpr(const TypeTraitExpr *e) {
+    assert(!e->isStoredAsBoolean() && !e->getType()->isIntegerType() &&
+           "expected a non-boolean type trait with a stored value");
 
-    // Emit the address of the first (and only) field in the comparison category
-    // type, and initialize it from the constant integer value produced above.
-    const FieldDecl *resultField = *cmpInfo.Record->field_begin();
-    LValue fieldLVal = cgf.emitLValueForFieldInitialization(
-        destLVal, resultField, resultField->getName());
-    cgf.emitStoreThroughLValue(RValue::get(resultScalar), fieldLVal);
+    const ComparisonCategoryInfo &cmpInfo =
+        cgf.getContext().CompCategories.getInfoForType(e->getType());
+    const auto result =
+        ComparisonCategoryResult(e->getAPValue().getInt().getZExtValue());
+    mlir::Location loc = cgf.getLoc(e->getSourceRange());
+    mlir::Value resultValue = cgf.getBuilder().getConstInt(
+        loc, cmpInfo.getValueInfo(result)->getIntValue());
 
-    // All done! The result is in the dest slot.
+    emitComparisonResult(e, loc, cmpInfo, resultValue);
   }
 
   void VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *e) {
@@ -999,6 +1007,21 @@ void AggExprEmitter::emitNullInitializationToLValue(mlir::Location loc,
   cgf.emitNullInitialization(loc, lv.getAddress(), lv.getType());
 }
 
+void AggExprEmitter::emitComparisonResult(const Expr *e, mlir::Location loc,
+                                          const ComparisonCategoryInfo &cmpInfo,
+                                          mlir::Value resultValue) {
+  // Create the return value in the destination slot.
+  ensureDest(loc, e->getType());
+  LValue destLVal = cgf.makeAddrLValue(dest.getAddress(), e->getType());
+
+  // Emit the address of the first (and only) field in the comparison category
+  // type, and initialize it from the constant integer value produced above.
+  const FieldDecl *resultField = *cmpInfo.Record->field_begin();
+  LValue fieldLVal = cgf.emitLValueForFieldInitialization(
+      destLVal, resultField, resultField->getName());
+  cgf.emitStoreThroughLValue(RValue::get(resultValue), fieldLVal);
+}
+
 void AggExprEmitter::VisitLambdaExpr(LambdaExpr *e) {
   CIRGenFunction::SourceLocRAIIObject loc{cgf, cgf.getLoc(e->getSourceRange())};
   AggValueSlot slot = ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType());
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 8d660a0a2c721..d08fee1f55be2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -901,6 +901,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
       return builder.getConstInt(loc, cgf.convertType(e->getType()),
                                  (uint64_t)e->getBoolValue());
     }
+    assert(e->getType()->isIntegerType() && "not a scalar type trait");
     return builder.getConstInt(loc, e->getAPValue().getInt());
   }
   mlir::Value
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index bc35ffdaad2bd..bc89f3fa37fd3 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -95,6 +95,10 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
                      Expr *ExprToVisit, ArrayRef<Expr *> Args,
                      Expr *ArrayFiller);
 
+  void EmitComparisonResult(const Expr *E,
+                            const ComparisonCategoryInfo &CmpInfo,
+                            llvm::Value *ResultValue);
+
   AggValueSlot::NeedsGCBarriers_t needsGC(QualType T) {
     if (CGF.getLangOpts().getGC() && TypeRequiresGCollection(T))
       return AggValueSlot::NeedsGCBarriers;
@@ -164,6 +168,7 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
   void VisitBinAssign(const BinaryOperator *E);
   void VisitBinComma(const BinaryOperator *E);
   void VisitBinCmp(const BinaryOperator *E);
+  void VisitTypeTraitExpr(const TypeTraitExpr *E);
   void VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) {
     Visit(E->getSemanticForm());
   }
@@ -1204,6 +1209,21 @@ static llvm::Value *EmitCompare(CGBuilderTy &Builder, CodeGenFunction &CGF,
                    "already been handled");
 }
 
+void AggExprEmitter::EmitComparisonResult(const Expr *E,
+                                          const ComparisonCategoryInfo &CmpInfo,
+                                          llvm::Value *ResultValue) {
+  // Create the return value in the destination slot.
+  EnsureDest(E->getType());
+  LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
+
+  // Emit the address of the first (and only) field in the comparison category
+  // type, and initialize it from the constant integer value selected above.
+  LValue FieldLV = CGF.EmitLValueForFieldInitialization(
+      DestLV, *CmpInfo.Record->field_begin());
+  CGF.EmitStoreThroughLValue(RValue::get(ResultValue), FieldLV,
+                             /*IsInit=*/true);
+}
+
 void AggExprEmitter::VisitBinCmp(const BinaryOperator *E) {
   using llvm::BasicBlock;
   using llvm::PHINode;
@@ -1271,17 +1291,22 @@ void AggExprEmitter::VisitBinCmp(const BinaryOperator *E) {
     Select = Builder.CreateSelect(
         EmitCmp(CK_Less), EmitCmpRes(CmpInfo.getLess()), SelectGT, "sel.lt");
   }
-  // Create the return value in the destination slot.
-  EnsureDest(E->getType());
-  LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
 
-  // Emit the address of the first (and only) field in the comparison category
-  // type, and initialize it from the constant integer value selected above.
-  LValue FieldLV = CGF.EmitLValueForFieldInitialization(
-      DestLV, *CmpInfo.Record->field_begin());
-  CGF.EmitStoreThroughLValue(RValue::get(Select), FieldLV, /*IsInit*/ true);
+  EmitComparisonResult(E, CmpInfo, Select);
+}
+
+void AggExprEmitter::VisitTypeTraitExpr(const TypeTraitExpr *E) {
+  assert(!E->isStoredAsBoolean() && E->getAPValue().isInt() &&
+         "expected a non-boolean type trait with a stored value");
+
+  const ComparisonCategoryInfo &CmpInfo =
+      CGF.getContext().CompCategories.getInfoForType(E->getType());
+  const auto Result =
+      ComparisonCategoryResult(E->getAPValue().getInt().getZExtValue());
+  llvm::Value *ResultValue =
+      Builder.getInt(CmpInfo.getValueInfo(Result)->getIntValue());
 
-  // All done! The result is in the Dest slot.
+  EmitComparisonResult(E, CmpInfo, ResultValue);
 }
 
 void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) {
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 67014904ffb37..d15892f8dc23d 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -786,6 +786,7 @@ class ScalarExprEmitter
     if (E->isStoredAsBoolean())
       return llvm::ConstantInt::get(ConvertType(E->getType()),
                                     E->getBoolValue());
+    assert(E->getType()->isIntegerType() && "not a scalar type trait");
     assert(E->getAPValue().isInt() && "APValue type not supported");
     return llvm::ConstantInt::get(ConvertType(E->getType()),
                                   E->getAPValue().getInt());
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 898e7ce71a311..8a3d4aa31787c 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -636,6 +636,26 @@ static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) {
   }
 }
 
+static ComparisonCategoryResult EvaluateTypeOrder(Sema &S, QualType LHS,
+                                                  QualType RHS) {
+  if (S.Context.hasSameType(LHS, RHS))
+    return ComparisonCategoryResult::Equal;
+
+  std::unique_ptr<MangleContext> MC(S.Context.createMangleContext());
+  SmallString<64> LhsName, RhsName;
+  {
+    llvm::raw_svector_ostream LhsOut(LhsName), RhsOut(RhsName);
+    MC->mangleCanonicalTypeName(LHS, LhsOut);
+    MC->mangleCanonicalTypeName(RHS, RhsOut);
+  }
+
+  int Result = LhsName.compare(RhsName);
+  if (Result == 0)
+    return ComparisonCategoryResult::Equal;
+  return Result > 0 ? ComparisonCategoryResult::Greater
+                    : ComparisonCategoryResult::Less;
+}
+
 static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
                                    SourceLocation KeyLoc,
                                    TypeSourceInfo *TInfo) {
@@ -1413,6 +1433,32 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
   return false;
 }
 
+static ExprResult
+EvaluateStrongOrderingTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc,
+                                ArrayRef<TypeSourceInfo *> Args,
+                                SourceLocation RParenLoc, bool IsDependent) {
+  QualType StrongOrdering = S.CheckComparisonCategoryType(
+      ComparisonCategoryType::StrongOrdering, KWLoc,
+      Sema::ComparisonCategoryUsage::Builtin);
+  if (StrongOrdering.isNull())
+    return ExprError();
+
+  if (IsDependent)
+    return TypeTraitExpr::Create(S.Context, StrongOrdering, KWLoc, Kind, Args,
+                                 RParenLoc, APValue());
+
+  switch (Kind) {
+  case clang::BTT_TypeOrder: {
+    ComparisonCategoryResult Result =
+        EvaluateTypeOrder(S, Args[0]->getType(), Args[1]->getType());
+    return TypeTraitExpr::Create(S.Context, StrongOrdering, KWLoc, Kind, Args,
+                                 RParenLoc, Result);
+  }
+  default:
+    llvm_unreachable("not a strong_ordering type trait");
+  }
+}
+
 namespace {
 void DiagnoseBuiltinDeprecation(Sema &S, TypeTrait Kind, SourceLocation KWLoc) {
   TypeTrait Replacement;
@@ -1468,26 +1514,6 @@ bool Sema::CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N) {
   return true;
 }
 
-static ComparisonCategoryResult EvaluateTypeOrder(Sema &S, QualType LHS,
-                                                  QualType RHS) {
-  if (S.Context.hasSameType(LHS, RHS))
-    return ComparisonCategoryResult::Equal;
-
-  std::unique_ptr<MangleContext> MC(S.Context.createMangleContext());
-  SmallString<64> LhsName, RhsName;
-  {
-    llvm::raw_svector_ostream LhsOut(LhsName), RhsOut(RhsName);
-    MC->mangleCanonicalTypeName(LHS, LhsOut);
-    MC->mangleCanonicalTypeName(RHS, RhsOut);
-  }
-
-  int Result = LhsName.compare(RhsName);
-  if (Result == 0)
-    return ComparisonCategoryResult::Equal;
-  return Result > 0 ? ComparisonCategoryResult::Greater
-                    : ComparisonCategoryResult::Less;
-}
-
 ExprResult Sema::ActOnBuiltinTypeOrder(SourceLocation KWLoc, ParsedType LhsTy,
                                        ParsedType RhsTy,
                                        SourceLocation RParenLoc) {
@@ -1509,7 +1535,7 @@ ExprResult Sema::BuildBuiltinTypeOrderExpr(SourceLocation KWLoc,
                                            SourceLocation RParenLoc) {
   QualType StrongOrdering =
       CheckComparisonCategoryType(ComparisonCategoryType::StrongOrdering, KWLoc,
-                                  ComparisonCategoryUsage::BuiltinTypeOrder);
+                                  ComparisonCategoryUsage::Builtin);
   if (StrongOrdering.isNull())
     return ExprError();
 
@@ -1533,11 +1559,14 @@ ExprResult Sema::BuildBuiltinTypeOrderExpr(SourceLocation KWLoc,
 enum class TypeTraitReturnType {
   Bool,
   SizeT,
+  StrongOrdering,
 };
 
 static TypeTraitReturnType GetReturnType(TypeTrait Kind) {
   if (Kind == TypeTrait::UTT_StructuredBindingSize)
     return TypeTraitReturnType::SizeT;
+  if (Kind == TypeTrait::BTT_TypeOrder)
+    return TypeTraitReturnType::StrongOrdering;
   return TypeTraitReturnType::Bool;
 }
 
@@ -1574,6 +1603,9 @@ ExprResult Sema::BuildTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
     return TypeTraitExpr::Create(Context, Context.getSizeType(), KWLoc, Kind,
                                  Args, RParenLoc, Result);
   }
+  case TypeTraitReturnType::StrongOrdering:
+    return EvaluateStrongOrderingTypeTrait(*this, Kind, KWLoc, Args, RParenLoc,
+                                           Dependent);
   }
   llvm_unreachable("unhandled type trait return type");
 }
diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 57c97b2852445..d48836d0c391b 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -368,6 +368,8 @@ std::optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
     const auto *TE = cast<TypeTraitExpr>(E);
     if (TE->isStoredAsBoolean())
       return makeTruthVal(TE->getBoolValue(), TE->getType());
+    if (!TE->getType()->isIntegerType())
+      return UnknownVal();
     assert(TE->getAPValue().isInt() && "APValue type not supported");
     return makeIntVal(TE->getAPValue().getInt());
   }
diff --git a/clang/test/AST/ast-dump-traits.cpp b/clang/test/AST/ast-dump-traits.cpp
index b844fd6bcc49c..cbd8d19445357 100644
--- a/clang/test/AST/ast-dump-traits.cpp
+++ b/clang/test/AST/ast-dump-traits.cpp
@@ -33,6 +33,28 @@ void test_unary_expr_or_type_trait() {
   (void) alignof(int);
   (void) __alignof(int);
 }
+
+namespace std {
+struct strong_ordering {
+  signed char value;
+
+  constexpr explicit strong_ordering(signed char value) : value(value) {}
+
+  static const strong_ordering less;
+  static const strong_ordering equal;
+  static const strong_ordering greater;
+};
+
+inline constexpr strong_ordering strong_ordering::less{-1};
+inline constexpr strong_ordering strong_ordering::equal{0};
+inline constexpr strong_ordering strong_ordering::greater{1};
+} // namespace std
+
+template <class T, class U>
+constexpr std::strong_ordering test_strong_ordering_type_trait() {
+  return __builtin_type_order(T, U);
+}
+
 // CHECK: TranslationUnitDecl {{.*}} <<invalid sloc>> <invalid sloc>{{( <undeserialized declarations>)?}}
 // CHECK: |-FunctionDecl {{.*}} <{{.*}}ast-dump-traits.cpp:10:1, line:18:1> line:10:6{{( imported)?}} test_type_trait 'void ()'
 // CHECK-NEXT: | `-CompoundStmt {{.*}} <col:24, line:18:1>
@@ -48,10 +70,10 @@ void test_unary_expr_or_type_trait() {
 // CHECK-NEXT: |   |   `-BuiltinType {{.*}} 'float'
 // CHECK-NEXT: |   `-CStyleCastExpr {{.*}} <line:17:3, col:47> 'void' <ToVoid>
 // CHECK-NEXT: |     `-TypeTraitExpr {{.*}} <col:10, col:47> 'bool' __is_constructible
-// CHECK-NEXT:         |-BuiltinType {{.*}} 'int'
-// CHECK-NEXT:         |-BuiltinType {{.*}} 'int'
-// CHECK-NEXT:         |-BuiltinType {{.*}} 'int'
-// CHECK-NEXT:         `-BuiltinType {{.*}} 'int'
+// CHECK-NEXT: |       |-BuiltinType {{.*}} 'int'
+// CHECK-NEXT: |       |-BuiltinType {{.*}} 'int'
+// CHECK-NEXT: |       |-BuiltinType {{.*}} 'int'
+// CHECK-NEXT: |       `-BuiltinType {{.*}} 'int'
 // CHECK-NEXT: |-FunctionDecl {{.*}} <line:20:1, line:23:1> line:20:6{{( imported)?}} test_array_type_trait 'void ()'
 // CHECK-NEXT: | `-CompoundStmt {{.*}} <col:30, line:23:1>
 // CHECK-NEXT: |   `-CStyleCastExpr {{.*}} <line:22:3, col:34> 'void' <ToVoid>
@@ -60,11 +82,22 @@ void test_unary_expr_or_type_trait() {
 // CHECK-NEXT: | `-CompoundStmt {{.*}} <col:30, line:28:1>
 // CHECK-NEXT: |   `-CStyleCastExpr {{.*}} <line:27:3, col:28> 'void' <ToVoid>
 // CHECK-NEXT: |     `-ExpressionTraitExpr {{.*}} <col:10, col:28> 'bool' __is_lvalue_expr
-// CHECK-NEXT: `-FunctionDecl {{.*}} <line:30:1, line:35:1> line:30:6{{( imported)?}} test_unary_expr_or_type_trait 'void ()'
-// CHECK-NEXT:   `-CompoundStmt {{.*}} <col:38, line:35:1>
-// CHECK-NEXT:     |-CStyleCastExpr {{.*}} <line:32:3, col:20> 'void' <ToVoid>
-// CHECK-NEXT:     | `-UnaryExprOrTypeTraitExpr {{.*}} <col:10, col:20> '__size_t':'unsigned long' sizeof 'int'
-// CHECK-NEXT:     |-CStyleCastExpr {{.*}} <line:33:3, col:21> 'void' <ToVoid>
-// CHECK-NEXT:     | `-UnaryExprOrTypeTraitExpr {{.*}} <col:10, col:21> '__size_t':'unsigned long' alignof 'int'
-// CHECK-NEXT:     `-CStyleCastExpr {{.*}} <line:34:3, col:23> 'void' <ToVoid>
-// CHECK-NEXT:       `-UnaryExprOrTypeTraitExpr {{.*}} <col:10, col:23> '__size_t':'unsigned long' __alignof 'int'
+// CHECK-NEXT: |-FunctionDecl {{.*}} <line:30:1, line:35:1> line:30:6{{( imported)?}} test_unary_expr_or_type_trait 'void ()'
+// CHECK-NEXT: | `-CompoundStmt {{.*}} <col:38, line:35:1>
+// CHECK-NEXT: |   |-CStyleCastExpr {{.*}} <line:32:3, col:20> 'void' <ToVoid>
+// CHECK-NEXT: |   | `-UnaryExprOrTypeTraitExpr {{.*}} <col:10, col:20> '__size_t':'unsigned long' sizeof 'int'
+// CHECK-NEXT: |   |-CStyleCastExpr {{.*}} <line:33:3, col:21> 'void' <ToVoid>
+// CHECK-NEXT: |   | `-UnaryExprOrTypeTraitExpr {{.*}} <col:10, col:21> '__size_t':'unsigned long' alignof 'int'
+// CHECK-NEXT: |   `-CStyleCastExpr {{.*}} <line:34:3, col:23> 'void' <ToVoid>
+// CHECK-NEXT: |     `-UnaryExprOrTypeTraitExpr {{.*}} <col:10, col:23> '__size_t':'unsigned long' __alignof 'int'
+// CHECK: `-FunctionTemplateDecl {{.*}} <line:53:1, line:56:1> line:54:32{{( imported)?}} test_strong_ordering_type_trait
+// CHECK-NEXT:   |-TemplateTypeParmDecl {{.*}} <line:53:11, col:17> col:17{{( imported)?}} referenced class depth 0 index 0 T
+// CHECK-NEXT:   |-TemplateTypeParmDecl {{.*}} <col:20, col:26> col:26{{( imported)?}} referenced class depth 0 index 1 U
+// CHECK-NEXT:   `-FunctionDecl {{.*}} <line:54:1, line:56:1> line:54:32{{( imported)?}} constexpr test_strong_ordering_type_trait 'std::strong_ordering ()'
+// CHECK-NEXT:     `-CompoundStmt {{.*}} <col:66, line:56:1>
+// CHECK-NEXT:       `-ReturnStmt {{.*}} <line:55:3, col:36>
+// CHECK-NEXT:         `-TypeTraitExpr {{.*}} <col:10, col:36> 'std::strong_ordering' __builtin_type_order2
+// CHECK-NEXT:           |-TemplateTypeParmType {{.*}} 'T' dependent{{( imported)?}} depth 0 index 0
+// CHECK-NEXT:           | `-TemplateTypeParm {{.*}} 'T'
+// CHECK-NEXT:           `-TemplateTypeParmType {{.*}} 'U' dependent{{( imported)?}} depth 0 index 1
+// CHECK-NEXT:             `-TemplateTypeParm {{.*}} 'U'
diff --git a/clang/test/CIR/CodeGen/cxx-traits.cpp b/clang/test/CIR/CodeGen/cxx-traits.cpp
index e078b8a178d2b..1a27e2c3a80cb 100644
--- a/clang/test/CIR/CodeGen/cxx-traits.cpp
+++ b/clang/test/CIR/CodeGen/cxx-traits.cpp
@@ -5,6 +5,25 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
 // RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
 
+namespace std {
+struct strong_ordering {
+  signed char value;
+
+  constexpr explicit strong_ordering(signed char value) : value(value) {}
+
+  static const strong_ordering less;
+  static const strong_ordering equal;
+  static const strong_ordering greater;
+};
+
+inline constexpr strong_ordering strong_ordering::less(-1);
+inline constexpr strong_ordering strong_ordering::equal(0);
+inline constexpr strong_ordering strong_ordering::greater(1);
+} // namespace std
+
+struct A {};
+struct B {};
+
 void expression_trait_expr() {
   bool a = __is_lvalue_expr(0);
 }
@@ -79,3 +98,45 @@ void array_type_trait_expr() {
 // OGCG: %[[B_ADDR:.*]] = alloca i64, align 8
 // OGCG: store i64 2, ptr %[[A_ADDR]], align 8
 // OGCG: store i64 20, ptr %[[B_ADDR]], align 8
+
+std::strong_ordering strong_ordering_type_trait_equal() {
+  return __builtin_type_order(int, int);
+}
+
+std::strong_ordering strong_ordering_type_trait_less() {
+  return __builtin_type_order(A, B);
+}
+
+std::strong_ordering strong_ordering_type_trait_greater() {
+  return __builtin_type_order(B, A);
+}
+
+// CIR-LABEL: cir.func {{.*}}@_Z32strong_ordering_type_trait_equalv
+// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s8i
+// CIR: cir.store {{.*}} %[[ZERO]], {{.*}} : !s8i, !cir.ptr<!s8i>
+
+// CIR-LABEL: cir.func {{.*}}@_Z31strong_ordering_type_trait_lessv
+// CIR: %[[MINUS_ONE:.*]] = cir.const #cir.int<-1> : !s8i
+// CIR: cir.store {{.*}} %[[MINUS_ONE]], {{.*}} : !s8i, !cir.ptr<!s8i>
+
+// CIR-LABEL: cir.func {{.*}}@_Z34strong_ordering_type_trait_greaterv
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s8i
+// CIR: cir.store {{.*}} %[[ONE]], {{.*}} : !s8i, !cir.ptr<!s8i>
+
+// LLVM-LABEL: define{{.*}} i8 @_Z32strong_ordering_type_trait_equalv()
+// LLVM: store i8 0
+
+// LLVM-LABEL: define{{.*}} i8 @_Z31strong_ordering_type_trait_lessv()
+// LLVM: store i8 -1
+
+// LLVM-LABEL: define{{.*}} i8 @_Z34strong_ordering_type_trait_greaterv()
+// LLVM: store i8 1
+
+// OGCG-LABEL: define{{.*}} i8 @_Z32strong_ordering_type_trait_equalv()
+// OGCG: store i8 0
+
+// OGCG-LABEL: define{{.*}} i8 @_Z31strong_ordering_type_trait_lessv()
+// OGCG: store i8 -1
+
+// OGCG-LABEL: define{{.*}} i8 @_Z34strong_ordering_type_trait_greaterv()
+// OGCG: store i8 1
diff --git a/clang/test/PCH/cxx-traits.cpp b/clang/test/PCH/cxx-traits.cpp
index 01b9e9302d790..b801aaec56009 100644
--- a/clang/test/PCH/cxx-traits.cpp
+++ b/clang/test/PCH/cxx-traits.cpp
@@ -3,6 +3,7 @@
 
 // RUN: %clang_cc1 -fms-extensions -x c++-header -std=c++11 -emit-pch -o %t %S/cxx-traits.h
 // RUN: %clang_cc1 -fms-extensions -std=c++11 -include-pch %t -DPCH -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fms-extensions -std=c++11 -include-pch %t -DPCH -fsyntax-only -verify -fexperimental-new-constant-interpreter %s
 
 #ifdef PCH
 // expected-no-diagnostics
@@ -14,6 +15,9 @@ bool _Is_empty_check = n::__is_empty<int>::__value;
 bool default_construct_int = n::is_trivially_constructible<int>::value;
 bool copy_construct_int = n::is_trivially_constructible<int, const int&>::value;
 
+static_assert(type_order<int, int>().value == std::__order::equal, "");
+static_assert(type_order<int, long>().value != std::__order::equal, "");
+
 // The built-ins should still work too:
 bool _is_abstract_result = __is_abstract(int);
 bool _is_aggregate_result = __is_aggregate(int);
diff --git a/clang/test/PCH/cxx-traits.h b/clang/test/PCH/cxx-traits.h
index 0a4bd09c363e9..1385018e4caa9 100644
--- a/clang/test/PCH/cxx-traits.h
+++ b/clang/test/PCH/cxx-traits.h
@@ -69,3 +69,26 @@ struct __is_volatile {};  // expected-warning {{made available}}
 
 
 }
+
+namespace std {
+enum class __order : signed char { less = -1, equal = 0, greater = 1 };
+
+struct strong_ordering {
+  __order value;
+
+  constexpr explicit strong_ordering(__order value) : value(value) {}
+
+  static const strong_ordering less;
+  static const strong_ordering equal;
+  static const strong_ordering greater;
+};
+
+constexpr strong_ordering strong_ordering::less(__order::less);
+constexpr strong_ordering strong_ordering::equal(__order::equal);
+constexpr strong_ordering strong_ordering::greater(__order::greater);
+} // namespace std
+
+template <class T, class U>
+constexpr std::strong_ordering type_order() {
+  return __builtin_type_order(T, U);
+}

>From 6a4cfe8a7e40e2d189574bfc6f90d26187ee5022 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Wed, 19 Aug 2026 04:23:44 +0200
Subject: [PATCH 4/8] revert node-based implementation

---
 clang/include/clang/AST/ASTNodeTraverser.h    |  5 ---
 clang/include/clang/AST/ComputeDependence.h   |  2 -
 clang/include/clang/AST/ExprCXX.h             | 43 -------------------
 clang/include/clang/AST/RecursiveASTVisitor.h |  5 ---
 clang/include/clang/AST/StmtDataCollectors.td |  6 ---
 clang/include/clang/Basic/StmtNodes.td        |  1 -
 clang/include/clang/Basic/TokenKinds.def      |  1 -
 clang/include/clang/Parse/Parser.h            |  3 --
 clang/include/clang/Sema/Sema.h               |  7 ---
 .../include/clang/Serialization/ASTBitCodes.h |  1 -
 clang/lib/AST/ASTImporter.cpp                 | 19 +-------
 clang/lib/AST/ASTStructuralEquivalence.cpp    |  8 ----
 clang/lib/AST/ComputeDependence.cpp           |  8 ----
 clang/lib/AST/Expr.cpp                        |  1 -
 clang/lib/AST/ExprClassification.cpp          |  1 -
 clang/lib/AST/ExprConstant.cpp                |  1 -
 clang/lib/AST/ItaniumMangle.cpp               |  1 -
 clang/lib/AST/StmtPrinter.cpp                 |  8 ----
 clang/lib/AST/StmtProfile.cpp                 |  6 ---
 clang/lib/Parse/ParseExpr.cpp                 |  4 --
 clang/lib/Parse/ParseExprCXX.cpp              | 33 --------------
 clang/lib/Sema/SemaExceptionSpec.cpp          |  1 -
 clang/lib/Sema/SemaTypeTraits.cpp             | 42 ------------------
 clang/lib/Sema/TreeTransform.h                | 34 +--------------
 clang/lib/Serialization/ASTReaderStmt.cpp     | 13 ------
 clang/lib/Serialization/ASTWriter.cpp         |  1 -
 clang/lib/Serialization/ASTWriterStmt.cpp     |  8 ----
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |  1 -
 clang/test/AST/builtin-type-order.cpp         | 35 ---------------
 clang/test/PCH/builtin-type-order.cpp         | 31 -------------
 clang/test/Parser/builtin_type_order.cpp      | 14 ------
 clang/tools/libclang/CIndex.cpp               |  6 ---
 clang/tools/libclang/CXCursor.cpp             |  1 -
 33 files changed, 2 insertions(+), 349 deletions(-)
 delete mode 100644 clang/test/AST/builtin-type-order.cpp
 delete mode 100644 clang/test/PCH/builtin-type-order.cpp
 delete mode 100644 clang/test/Parser/builtin_type_order.cpp

diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index e7c02bdae2d74..a8a73c5b72d33 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -934,11 +934,6 @@ class ASTNodeTraverser
       Visit(A->getType());
   }
 
-  void VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *E) {
-    Visit(E->getLHSType());
-    Visit(E->getRHSType());
-  }
-
   void VisitLambdaExpr(const LambdaExpr *Node) {
     if (Traversal == TK_IgnoreUnlessSpelledInSource) {
       for (unsigned I = 0, N = Node->capture_size(); I != N; ++I) {
diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h
index c78cf273dc17e..3a3c86842501a 100644
--- a/clang/include/clang/AST/ComputeDependence.h
+++ b/clang/include/clang/AST/ComputeDependence.h
@@ -83,7 +83,6 @@ class CXXDependentScopeMemberExpr;
 class MaterializeTemporaryExpr;
 class CXXFoldExpr;
 class CXXParenListInitExpr;
-class BuiltinTypeOrderExpr;
 class TypeTraitExpr;
 class ConceptSpecializationExpr;
 class SYCLUniqueStableNameExpr;
@@ -179,7 +178,6 @@ ExprDependence computeDependence(CXXDependentScopeMemberExpr *E);
 ExprDependence computeDependence(MaterializeTemporaryExpr *E);
 ExprDependence computeDependence(CXXFoldExpr *E);
 ExprDependence computeDependence(CXXParenListInitExpr *E);
-ExprDependence computeDependence(BuiltinTypeOrderExpr *E);
 ExprDependence computeDependence(TypeTraitExpr *E);
 ExprDependence computeDependence(ConceptSpecializationExpr *E,
                                  bool ValueDependent);
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 2704df16acb87..56ce2c9dfe987 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2887,49 +2887,6 @@ class CXXPseudoDestructorExpr : public Expr {
   }
 };
 
-/// Represents a C++26 __builtin_type_order(T, U) expression. Used to implement
-/// std::type_order.
-class BuiltinTypeOrderExpr final : public Expr {
-  friend class ASTStmtReader;
-
-  SourceLocation Loc;
-  SourceLocation RParenLoc;
-  TypeSourceInfo *LHS;
-  TypeSourceInfo *RHS;
-
-public:
-  BuiltinTypeOrderExpr(QualType T, SourceLocation Loc, TypeSourceInfo *LHS,
-                       TypeSourceInfo *RHS, SourceLocation RParenLoc)
-      : Expr(BuiltinTypeOrderExprClass, T, VK_PRValue, OK_Ordinary), Loc(Loc),
-        RParenLoc(RParenLoc), LHS(LHS), RHS(RHS) {
-    setDependence(computeDependence(this));
-  }
-
-  explicit BuiltinTypeOrderExpr(EmptyShell Empty)
-      : Expr(BuiltinTypeOrderExprClass, Empty) {}
-
-  TypeSourceInfo *getLHSTypeInfo() const { return LHS; }
-  TypeSourceInfo *getRHSTypeInfo() const { return RHS; }
-
-  QualType getLHSType() const { return getLHSTypeInfo()->getType(); }
-  QualType getRHSType() const { return getRHSTypeInfo()->getType(); }
-
-  SourceLocation getBeginLoc() const LLVM_READONLY { return Loc; }
-  SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; }
-
-  static bool classof(const Stmt *T) {
-    return T->getStmtClass() == BuiltinTypeOrderExprClass;
-  }
-
-  child_range children() {
-    return child_range(child_iterator(), child_iterator());
-  }
-
-  const_child_range children() const {
-    return const_child_range(const_child_iterator(), const_child_iterator());
-  }
-};
-
 /// A type trait used in the implementation of various C++11 and
 /// Library TR1 trait templates.
 ///
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index e204da9ca3899..617990b82edca 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2865,11 +2865,6 @@ DEF_TRAVERSE_STMT(TypeTraitExpr, {
     TRY_TO(TraverseTypeLoc(S->getArg(I)->getTypeLoc()));
 })
 
-DEF_TRAVERSE_STMT(BuiltinTypeOrderExpr, {
-  TRY_TO(TraverseTypeLoc(S->getLHSTypeInfo()->getTypeLoc()));
-  TRY_TO(TraverseTypeLoc(S->getRHSTypeInfo()->getTypeLoc()));
-})
-
 DEF_TRAVERSE_STMT(ArrayTypeTraitExpr, {
   TRY_TO(TraverseTypeLoc(S->getQueriedTypeSourceInfo()->getTypeLoc()));
 })
diff --git a/clang/include/clang/AST/StmtDataCollectors.td b/clang/include/clang/AST/StmtDataCollectors.td
index 7863cca48d079..abf4b5f34d349 100644
--- a/clang/include/clang/AST/StmtDataCollectors.td
+++ b/clang/include/clang/AST/StmtDataCollectors.td
@@ -37,12 +37,6 @@ class TypeTraitExpr {
       addData(S->getArg(i)->getType());
   }];
 }
-class BuiltinTypeOrderExpr {
-  code Code = [{
-    addData(S->getLHSType());
-    addData(S->getRHSType());
-  }];
-}
 
 //--- Calls --------------------------------------------------------------//
 class CallExpr {
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index 0c0f82f944ceb..6df244d7a8c90 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -150,7 +150,6 @@ def CXXStdInitializerListExpr : StmtNode<Expr>;
 def CXXNewExpr : StmtNode<Expr>;
 def CXXDeleteExpr : StmtNode<Expr>;
 def CXXPseudoDestructorExpr : StmtNode<Expr>;
-def BuiltinTypeOrderExpr : StmtNode<Expr>;
 def TypeTraitExpr : StmtNode<Expr>;
 def ArrayTypeTraitExpr : StmtNode<Expr>;
 def ExpressionTraitExpr : StmtNode<Expr>;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 38995aca639a5..dc9c7d8109467 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -469,7 +469,6 @@ KEYWORD(__builtin_FUNCSIG           , KEYMS)
 KEYWORD(__builtin_LINE              , KEYALL)
 KEYWORD(__builtin_COLUMN            , KEYALL)
 KEYWORD(__builtin_source_location   , KEYCXX)
-KEYWORD(__builtin_type_order        , KEYCXX)
 
 KEYWORD(__builtin_va_arg            , KEYALL)
 KEYWORD(__extension__               , KEYALL)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index feb39ecb5ca55..163aa483a84e3 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5195,9 +5195,6 @@ class Parser : public CodeCompletionHandler {
   ///
   ExprResult ParseTypeTrait();
 
-  /// Parse __builtin_type_order(T, U), used to implement C++26 std::type_order.
-  ExprResult ParseBuiltinTypeOrder();
-
   //===--------------------------------------------------------------------===//
   // Embarcadero: Arary and Expression Traits
 
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4b41f3acdfb6e..63988e110168c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8725,13 +8725,6 @@ class Sema final : public SemaBase {
 
   bool CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N);
 
-  ExprResult ActOnBuiltinTypeOrder(SourceLocation KWLoc, ParsedType LhsTy,
-                                   ParsedType RhsTy, SourceLocation RParenLoc);
-  ExprResult BuildBuiltinTypeOrderExpr(SourceLocation KWLoc,
-                                       TypeSourceInfo *LhsT,
-                                       TypeSourceInfo *RhsT,
-                                       SourceLocation RParenLoc);
-
   /// Parsed one of the type trait support pseudo-functions.
   ExprResult ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
                             ArrayRef<ParsedType> Args,
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 191918df5e5e7..b582cbdadc070 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1938,7 +1938,6 @@ enum StmtCode {
 
   EXPR_OPAQUE_VALUE,                // OpaqueValueExpr
   EXPR_BINARY_CONDITIONAL_OPERATOR, // BinaryConditionalOperator
-  EXPR_BUILTIN_TYPE_ORDER,          // BuiltinTypeOrderExpr
   EXPR_TYPE_TRAIT,                  // TypeTraitExpr
   EXPR_ARRAY_TYPE_TRAIT,            // ArrayTypeTraitIntExpr
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index e85f0c49e8807..5be9ce780aec5 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -696,9 +696,7 @@ namespace clang {
     ExpectedStmt VisitArrayInitIndexExpr(ArrayInitIndexExpr *E);
     ExpectedStmt VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E);
     ExpectedStmt VisitCXXNamedCastExpr(CXXNamedCastExpr *E);
-    ExpectedStmt
-    VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *E);
-    ExpectedStmt VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E);
+    ExpectedStmt VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *E);
     ExpectedStmt VisitTypeTraitExpr(TypeTraitExpr *E);
     ExpectedStmt VisitCXXTypeidExpr(CXXTypeidExpr *E);
     ExpectedStmt VisitCXXFoldExpr(CXXFoldExpr *E);
@@ -9159,21 +9157,6 @@ ExpectedStmt ASTNodeImporter::VisitSubstNonTypeTemplateParmExpr(
       ToParamType, E->getIndex(), E->getPackIndex(), E->getFinal());
 }
 
-ExpectedStmt
-ASTNodeImporter::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
-  Error Err = Error::success();
-  auto ToType = importChecked(Err, E->getType());
-  auto ToBeginLoc = importChecked(Err, E->getBeginLoc());
-  auto ToEndLoc = importChecked(Err, E->getEndLoc());
-  auto *ToLhs = importChecked(Err, E->getLHSTypeInfo());
-  auto *ToRhs = importChecked(Err, E->getRHSTypeInfo());
-  if (Err)
-    return std::move(Err);
-
-  return new (Importer.getToContext())
-      BuiltinTypeOrderExpr(ToType, ToBeginLoc, ToLhs, ToRhs, ToEndLoc);
-}
-
 ExpectedStmt ASTNodeImporter::VisitTypeTraitExpr(TypeTraitExpr *E) {
   Error Err = Error::success();
   auto ToType = importChecked(Err, E->getType());
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index e9292792bc823..d8bbfbe5dac72 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -351,14 +351,6 @@ class StmtComparer {
     return true;
   }
 
-  bool IsStmtEquivalent(const BuiltinTypeOrderExpr *E1,
-                        const BuiltinTypeOrderExpr *E2) {
-    return IsStructurallyEquivalent(Context, E1->getLHSType(),
-                                    E2->getLHSType()) &&
-           IsStructurallyEquivalent(Context, E1->getRHSType(),
-                                    E2->getRHSType());
-  }
-
   bool IsStmtEquivalent(const CXXDependentScopeMemberExpr *E1,
                         const CXXDependentScopeMemberExpr *E2) {
     if (!IsStructurallyEquivalent(Context, E1->getMember(), E2->getMember())) {
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 8e14387641030..7e6bd69711c5b 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -931,14 +931,6 @@ ExprDependence clang::computeDependence(TypeTraitExpr *E) {
   return D;
 }
 
-ExprDependence clang::computeDependence(BuiltinTypeOrderExpr *E) {
-  ExprDependence D = ExprDependence::None;
-  for (TypeSourceInfo *Arg : {E->getLHSTypeInfo(), E->getRHSTypeInfo()})
-    D |= toExprDependenceAsWritten(Arg->getType()->getDependence()) &
-         ~ExprDependence::Type;
-  return D;
-}
-
 ExprDependence clang::computeDependence(ConceptSpecializationExpr *E,
                                         bool ValueDependent) {
   auto TA = TemplateArgumentDependence::None;
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 55a5d8214e5da..5d7ee4710481c 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3747,7 +3747,6 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
   case CXXNullPtrLiteralExprClass:
   case CXXThisExprClass:
   case CXXScalarValueInitExprClass:
-  case BuiltinTypeOrderExprClass:
   case TypeTraitExprClass:
   case ArrayTypeTraitExprClass:
   case ExpressionTraitExprClass:
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index 37d799e329df0..ef071cdef66b6 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -192,7 +192,6 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
   case Expr::FloatingLiteralClass:
   case Expr::CXXNoexceptExprClass:
   case Expr::CXXScalarValueInitExprClass:
-  case Expr::BuiltinTypeOrderExprClass:
   case Expr::TypeTraitExprClass:
   case Expr::ArrayTypeTraitExprClass:
   case Expr::ExpressionTraitExprClass:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 2053ae3beb1ec..d6c4a5d21b46e 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -22460,7 +22460,6 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
   case Expr::ObjCBoolLiteralExprClass:
   case Expr::CXXBoolLiteralExprClass:
   case Expr::CXXScalarValueInitExprClass:
-  case Expr::BuiltinTypeOrderExprClass:
   case Expr::TypeTraitExprClass:
   case Expr::ConceptSpecializationExprClass:
   case Expr::RequiresExprClass:
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 126c1b67452c5..f6c4ca1ae6ba8 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5056,7 +5056,6 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   case Expr::StmtExprClass:
   case Expr::ArrayTypeTraitExprClass:
   case Expr::ExpressionTraitExprClass:
-  case Expr::BuiltinTypeOrderExprClass:
   case Expr::VAArgExprClass:
   case Expr::CUDAKernelCallExprClass:
   case Expr::AsTypeExprClass:
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 44acfc28113c4..eeb377c794e05 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2708,14 +2708,6 @@ void StmtPrinter::VisitTypeTraitExpr(TypeTraitExpr *E) {
   OS << ")";
 }
 
-void StmtPrinter::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
-  OS << "__builtin_type_order(";
-  E->getLHSType().print(OS, Policy);
-  OS << ", ";
-  E->getRHSType().print(OS, Policy);
-  OS << ')';
-}
-
 void StmtPrinter::VisitArrayTypeTraitExpr(ArrayTypeTraitExpr *E) {
   OS << getTraitSpelling(E->getTrait()) << '(';
   E->getQueriedType().print(OS, Policy);
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index d2ff5f6742321..45c962aa27553 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2320,12 +2320,6 @@ void StmtProfiler::VisitTypeTraitExpr(const TypeTraitExpr *S) {
     VisitType(S->getArg(I)->getType());
 }
 
-void StmtProfiler::VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *S) {
-  VisitExpr(S);
-  VisitType(S->getLHSType());
-  VisitType(S->getRHSType());
-}
-
 void StmtProfiler::VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *S) {
   VisitExpr(S);
   ID.AddInteger(S->getTrait());
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index a84de3a72170c..87cd7a01451cf 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1523,10 +1523,6 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand,
     break;
   }
 
-  case tok::kw___builtin_type_order:
-    Res = ParseBuiltinTypeOrder();
-    break;
-
 #define TYPE_TRAIT(N,Spelling,K) \
   case tok::kw_##Spelling:
 #include "clang/Basic/TokenKinds.def"
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 57e4f2096a9d9..f9a0dcc7d53af 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -3490,39 +3490,6 @@ static ExpressionTrait ExpressionTraitFromTokKind(tok::TokenKind kind) {
   }
 }
 
-ExprResult Parser::ParseBuiltinTypeOrder() {
-  SourceLocation Loc = ConsumeToken();
-
-  BalancedDelimiterTracker Parens(*this, tok::l_paren);
-  if (Parens.expectAndConsume())
-    return ExprError();
-
-  TypeResult LHS = ParseTypeName(/*SourceRange=*/nullptr,
-                                 DeclaratorContext::TemplateTypeArg);
-  if (LHS.isInvalid()) {
-    Parens.skipToEnd();
-    return ExprError();
-  }
-
-  if (ExpectAndConsume(tok::comma)) {
-    Parens.skipToEnd();
-    return ExprError();
-  }
-
-  TypeResult RHS = ParseTypeName(/*SourceRange=*/nullptr,
-                                 DeclaratorContext::TemplateTypeArg);
-  if (RHS.isInvalid()) {
-    Parens.skipToEnd();
-    return ExprError();
-  }
-
-  if (Parens.consumeClose())
-    return ExprError();
-
-  return Actions.ActOnBuiltinTypeOrder(Loc, LHS.get(), RHS.get(),
-                                       Parens.getCloseLocation());
-}
-
 ExprResult Parser::ParseTypeTrait() {
   tok::TokenKind Kind = Tok.getKind();
 
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 213aa6701fdb0..d41137ddc85d5 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1390,7 +1390,6 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::AddrLabelExprClass:
   case Expr::ArrayTypeTraitExprClass:
   case Expr::AtomicExprClass:
-  case Expr::BuiltinTypeOrderExprClass:
   case Expr::TypeTraitExprClass:
   case Expr::CXXBoolLiteralExprClass:
   case Expr::CXXNoexceptExprClass:
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 8a3d4aa31787c..557fc7b4fb33a 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1514,48 +1514,6 @@ bool Sema::CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N) {
   return true;
 }
 
-ExprResult Sema::ActOnBuiltinTypeOrder(SourceLocation KWLoc, ParsedType LhsTy,
-                                       ParsedType RhsTy,
-                                       SourceLocation RParenLoc) {
-  SmallVector<TypeSourceInfo *, 2> Args;
-  for (auto ArgT : {LhsTy, RhsTy}) {
-    TypeSourceInfo *TInfo;
-    QualType T = GetTypeFromParser(ArgT, &TInfo);
-    if (!TInfo)
-      TInfo = Context.getTrivialTypeSourceInfo(T, KWLoc);
-
-    Args.push_back(TInfo);
-  }
-  return BuildBuiltinTypeOrderExpr(KWLoc, Args[0], Args[1], RParenLoc);
-}
-
-ExprResult Sema::BuildBuiltinTypeOrderExpr(SourceLocation KWLoc,
-                                           TypeSourceInfo *LhsT,
-                                           TypeSourceInfo *RhsT,
-                                           SourceLocation RParenLoc) {
-  QualType StrongOrdering =
-      CheckComparisonCategoryType(ComparisonCategoryType::StrongOrdering, KWLoc,
-                                  ComparisonCategoryUsage::Builtin);
-  if (StrongOrdering.isNull())
-    return ExprError();
-
-  QualType LHS = LhsT->getType();
-  QualType RHS = RhsT->getType();
-  if (LHS->isDependentType() || RHS->isDependentType())
-    return new (Context)
-        BuiltinTypeOrderExpr(StrongOrdering, KWLoc, LhsT, RhsT, RParenLoc);
-
-  ComparisonCategoryResult Result = EvaluateTypeOrder(*this, LHS, RHS);
-  VarDecl *ResultVD = Context.CompCategories.getInfoForType(StrongOrdering)
-                          .getValueInfo(Result)
-                          ->VD;
-  Expr *ResultExpr =
-      BuildDeclRefExpr(ResultVD, ResultVD->getType(), VK_LValue, KWLoc);
-  return PerformCopyInitialization(
-      InitializedEntity::InitializeTemporary(StrongOrdering), KWLoc,
-      ResultExpr);
-}
-
 enum class TypeTraitReturnType {
   Bool,
   SizeT,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index aa630a55fa965..454e497c385fe 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3567,17 +3567,6 @@ class TreeTransform {
                                     Operand);
   }
 
-  /// Build a new type ordering expression.
-  ///
-  /// By default, performs semantic analysis to build the new expression.
-  /// Subclasses may override this routine to provide different behavior.
-  ExprResult RebuildBuiltinTypeOrderExpr(SourceLocation StartLoc,
-                                         TypeSourceInfo *LhsT,
-                                         TypeSourceInfo *RhsT,
-                                         SourceLocation RParenLoc) {
-    return getSema().BuildBuiltinTypeOrderExpr(StartLoc, LhsT, RhsT, RParenLoc);
-  }
-
   /// Build a new type trait expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -15496,28 +15485,7 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr(UnresolvedLookupExpr *Old,
                                             Old->requiresADL(), &TransArgs);
 }
 
-template <typename Derived>
-ExprResult
-TreeTransform<Derived>::TransformBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
-  SmallVector<TypeSourceInfo *, 2> Args;
-  bool ArgChanged = false;
-  for (TypeSourceInfo *From : {E->getLHSTypeInfo(), E->getRHSTypeInfo()}) {
-    TypeSourceInfo *To = getDerived().TransformType(From);
-    if (!To)
-      return ExprError();
-
-    Args.push_back(To);
-    ArgChanged |= To != From;
-  }
-
-  if (!getDerived().AlwaysRebuild() && !ArgChanged)
-    return E;
-
-  return getDerived().RebuildBuiltinTypeOrderExpr(E->getBeginLoc(), Args[0],
-                                                  Args[1], E->getEndLoc());
-}
-
-template <typename Derived>
+template<typename Derived>
 ExprResult TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
   bool ArgChanged = false;
   SmallVector<TypeSourceInfo *, 4> Args;
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 09572ffef827b..1afde95b05290 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2225,15 +2225,6 @@ void ASTStmtReader::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
   E->NamingClass = readDeclAs<CXXRecordDecl>();
 }
 
-void ASTStmtReader::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
-  VisitExpr(E);
-  SourceRange Range = readSourceRange();
-  E->Loc = Range.getBegin();
-  E->RParenLoc = Range.getEnd();
-  E->LHS = readTypeSourceInfo();
-  E->RHS = readTypeSourceInfo();
-}
-
 void ASTStmtReader::VisitTypeTraitExpr(TypeTraitExpr *E) {
   VisitExpr(E);
   E->TypeTraitExprBits.IsBooleanTypeTrait = Record.readInt();
@@ -4461,10 +4452,6 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
     }
 
-    case EXPR_BUILTIN_TYPE_ORDER:
-      S = new (Context) BuiltinTypeOrderExpr(Empty);
-      break;
-
     case EXPR_TYPE_TRAIT:
       S = TypeTraitExpr::CreateDeserialized(
           Context, Record[ASTStmtReader::NumExprFields],
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 1c7df49705658..f34b2ff182bc8 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -866,7 +866,6 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream,
   RECORD(EXPR_CXX_NOEXCEPT);
   RECORD(EXPR_OPAQUE_VALUE);
   RECORD(EXPR_BINARY_CONDITIONAL_OPERATOR);
-  RECORD(EXPR_BUILTIN_TYPE_ORDER);
   RECORD(EXPR_TYPE_TRAIT);
   RECORD(EXPR_ARRAY_TYPE_TRAIT);
   RECORD(EXPR_PACK_EXPANSION);
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 1983e7fef73a9..3854eabd62a46 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2244,14 +2244,6 @@ void ASTStmtWriter::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
   }
 }
 
-void ASTStmtWriter::VisitBuiltinTypeOrderExpr(BuiltinTypeOrderExpr *E) {
-  VisitExpr(E);
-  Record.AddSourceRange(E->getSourceRange());
-  Record.AddTypeSourceInfo(E->getLHSTypeInfo());
-  Record.AddTypeSourceInfo(E->getRHSTypeInfo());
-  Code = serialization::EXPR_BUILTIN_TYPE_ORDER;
-}
-
 void ASTStmtWriter::VisitTypeTraitExpr(TypeTraitExpr *E) {
   VisitExpr(E);
   Record.push_back(E->TypeTraitExprBits.IsBooleanTypeTrait);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 64ff7766ba9a4..328ed5b23dd83 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1699,7 +1699,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::DependentScopeDeclRefExprClass:
     case Stmt::ArrayTypeTraitExprClass:
     case Stmt::ExpressionTraitExprClass:
-    case Stmt::BuiltinTypeOrderExprClass:
     case Stmt::UnresolvedLookupExprClass:
     case Stmt::UnresolvedMemberExprClass:
     case Stmt::RecoveryExprClass:
diff --git a/clang/test/AST/builtin-type-order.cpp b/clang/test/AST/builtin-type-order.cpp
deleted file mode 100644
index 7b2447e647273..0000000000000
--- a/clang/test/AST/builtin-type-order.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -ast-print %s | FileCheck %s --check-prefix=PRINT
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -ast-dump -ast-dump-filter=dependent_order %s | FileCheck %s --check-prefix=AST
-
-namespace std {
-enum class __order : signed char { less = -1, equal = 0, greater = 1 };
-
-struct strong_ordering {
-  __order value;
-
-  constexpr explicit strong_ordering(__order value) : value(value) {}
-
-  static const strong_ordering less;
-  static const strong_ordering equal;
-  static const strong_ordering greater;
-};
-
-inline constexpr strong_ordering strong_ordering::less(__order::less);
-inline constexpr strong_ordering strong_ordering::equal(__order::equal);
-inline constexpr strong_ordering strong_ordering::greater(__order::greater);
-} // namespace std
-
-template <class T, class U>
-constexpr std::strong_ordering dependent_order() {
-  return __builtin_type_order(T, U);
-}
-
-// PRINT: template <class T, class U> constexpr std::strong_ordering dependent_order()
-// PRINT: return __builtin_type_order(T, U);
-
-// AST-LABEL: FunctionTemplateDecl {{.*}} dependent_order
-// AST: BuiltinTypeOrderExpr {{.*}} 'std::strong_ordering'
-// AST-NEXT: |-TemplateTypeParmType {{.*}} 'T' dependent
-// AST-NEXT: | `-TemplateTypeParm {{.*}} 'T'
-// AST-NEXT: `-TemplateTypeParmType {{.*}} 'U' dependent
-// AST-NEXT:   `-TemplateTypeParm {{.*}} 'U'
diff --git a/clang/test/PCH/builtin-type-order.cpp b/clang/test/PCH/builtin-type-order.cpp
deleted file mode 100644
index f948028bb494f..0000000000000
--- a/clang/test/PCH/builtin-type-order.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// RUN: %clang_cc1 -std=c++20 -emit-pch -o %t %s
-// RUN: %clang_cc1 -std=c++20 -include-pch %t -DTEST %s -verify
-// RUN: %clang_cc1 -std=c++20 -include-pch %t -DTEST %s -verify -fexperimental-new-constant-interpreter
-
-#ifndef TEST
-namespace std {
-enum class __order : signed char { less = -1, equal = 0, greater = 1 };
-
-struct strong_ordering {
-  __order value;
-
-  constexpr explicit strong_ordering(__order value) : value(value) {}
-
-  static const strong_ordering less;
-  static const strong_ordering equal;
-  static const strong_ordering greater;
-};
-
-inline constexpr strong_ordering strong_ordering::less(__order::less);
-inline constexpr strong_ordering strong_ordering::equal(__order::equal);
-inline constexpr strong_ordering strong_ordering::greater(__order::greater);
-} // namespace std
-
-template <class T, class U>
-constexpr auto type_order = __builtin_type_order(T, U);
-#else
-static_assert(type_order<int, int>.value == std::__order::equal);
-static_assert(type_order<int, long>.value != std::__order::equal);
-
-// expected-no-diagnostics
-#endif
diff --git a/clang/test/Parser/builtin_type_order.cpp b/clang/test/Parser/builtin_type_order.cpp
deleted file mode 100644
index 293a6b930681b..0000000000000
--- a/clang/test/Parser/builtin_type_order.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
-
-template <typename... T>
-void syntax_errors() {
-  (void)__builtin_type_order(int); // expected-error {{expected ','}}
-  (void)__builtin_type_order(T...); // expected-error {{expected ','}}
-
-  (void)__builtin_type_order(int, long, char); 
-  // expected-error at -1 {{expected ')'}} \
-  //   expected-note at -1 {{to match this '('}}
-  (void)__builtin_type_order(int, T...);
-  // expected-error at -1 {{expected ')'}} \
-  //   expected-note at -1 {{to match this '('}}
-}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index e424bf412adc8..e485e6e2e5547 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -2128,7 +2128,6 @@ class EnqueueVisitor : public ConstStmtVisitor<EnqueueVisitor, void>,
   void VisitStmt(const Stmt *S);
   void VisitSwitchStmt(const SwitchStmt *S);
   void VisitWhileStmt(const WhileStmt *W);
-  void VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *E);
   void VisitTypeTraitExpr(const TypeTraitExpr *E);
   void VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E);
   void VisitExpressionTraitExpr(const ExpressionTraitExpr *E);
@@ -3272,11 +3271,6 @@ void EnqueueVisitor::VisitTypeTraitExpr(const TypeTraitExpr *E) {
     AddTypeLoc(E->getArg(I - 1));
 }
 
-void EnqueueVisitor::VisitBuiltinTypeOrderExpr(const BuiltinTypeOrderExpr *E) {
-  AddTypeLoc(E->getRHSTypeInfo());
-  AddTypeLoc(E->getLHSTypeInfo());
-}
-
 void EnqueueVisitor::VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {
   AddTypeLoc(E->getQueriedTypeSourceInfo());
 }
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index 421630c764ebc..e27964d0ad6e1 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -304,7 +304,6 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
   case Stmt::AsTypeExprClass:
   case Stmt::AtomicExprClass:
   case Stmt::BinaryConditionalOperatorClass:
-  case Stmt::BuiltinTypeOrderExprClass:
   case Stmt::TypeTraitExprClass:
   case Stmt::CoawaitExprClass:
   case Stmt::DependentCoawaitExprClass:

>From c68a55b918e2ca8f7b5012d4f76413d4933003b8 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Wed, 19 Aug 2026 04:24:12 +0200
Subject: [PATCH 5/8] rename new trait

---
 clang/include/clang/Basic/BuiltinTraits.td | 2 +-
 clang/test/AST/ast-dump-traits.cpp         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinTraits.td b/clang/include/clang/Basic/BuiltinTraits.td
index 17a8b4a4623d1..b4a0c7379d373 100644
--- a/clang/include/clang/Basic/BuiltinTraits.td
+++ b/clang/include/clang/Basic/BuiltinTraits.td
@@ -127,7 +127,7 @@ def TypeCompatible : BinaryTrait {
 }
 
 def TypeOrder : BinaryTrait {
-  let Spelling = "__builtin_type_order2";
+  let Spelling = "__builtin_type_order";
   let KeyFlags = [KEYCXX];
 }
 
diff --git a/clang/test/AST/ast-dump-traits.cpp b/clang/test/AST/ast-dump-traits.cpp
index cbd8d19445357..1826268272545 100644
--- a/clang/test/AST/ast-dump-traits.cpp
+++ b/clang/test/AST/ast-dump-traits.cpp
@@ -96,7 +96,7 @@ constexpr std::strong_ordering test_strong_ordering_type_trait() {
 // CHECK-NEXT:   `-FunctionDecl {{.*}} <line:54:1, line:56:1> line:54:32{{( imported)?}} constexpr test_strong_ordering_type_trait 'std::strong_ordering ()'
 // CHECK-NEXT:     `-CompoundStmt {{.*}} <col:66, line:56:1>
 // CHECK-NEXT:       `-ReturnStmt {{.*}} <line:55:3, col:36>
-// CHECK-NEXT:         `-TypeTraitExpr {{.*}} <col:10, col:36> 'std::strong_ordering' __builtin_type_order2
+// CHECK-NEXT:         `-TypeTraitExpr {{.*}} <col:10, col:36> 'std::strong_ordering' __builtin_type_order
 // CHECK-NEXT:           |-TemplateTypeParmType {{.*}} 'T' dependent{{( imported)?}} depth 0 index 0
 // CHECK-NEXT:           | `-TemplateTypeParm {{.*}} 'T'
 // CHECK-NEXT:           `-TemplateTypeParmType {{.*}} 'U' dependent{{( imported)?}} depth 0 index 1

>From a0121bd4fb59521f33537c69be603d403f51dc26 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Wed, 19 Aug 2026 04:45:29 +0200
Subject: [PATCH 6/8] massage the diagnostic

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
 clang/test/SemaCXX/builtin-type-order.cpp        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9356416eed302..d70e640d084f1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13354,7 +13354,7 @@ def warn_invalid_default_version_priority
 
 // three-way comparison operator diagnostics
 def err_implied_comparison_category_type_not_found : Error<
-  "cannot %select{use builtin operator '<=>'|default 'operator<=>'|retrieve type order}1 "
+  "cannot %select{use builtin operator '<=>'|default 'operator<=>'|compute order}1 "
   "because type '%0' was not found; include <compare>">;
 def err_spaceship_argument_narrowing : Error<
   "argument to 'operator<=>' "
diff --git a/clang/test/SemaCXX/builtin-type-order.cpp b/clang/test/SemaCXX/builtin-type-order.cpp
index 2de69008b9e23..42c8c16bfbace 100644
--- a/clang/test/SemaCXX/builtin-type-order.cpp
+++ b/clang/test/SemaCXX/builtin-type-order.cpp
@@ -4,7 +4,7 @@
 // RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -std=c++20 -fsyntax-only -verify -DMICROSOFT -fexperimental-new-constant-interpreter %s
 
 constexpr auto missing = __builtin_type_order(int, long);
-// expected-error at -1 {{cannot retrieve type order because type 'std::strong_ordering' was not found; include <compare>}}
+// expected-error at -1 {{cannot compute order because type 'std::strong_ordering' was not found; include <compare>}}
 
 namespace std {
   struct strong_ordering {

>From 0ad10cee7da901dc53779620ea16ca6bbcd0af1f Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Wed, 19 Aug 2026 05:05:25 +0200
Subject: [PATCH 7/8] fix column mismatch

---
 clang/test/AST/ast-dump-traits.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/test/AST/ast-dump-traits.cpp b/clang/test/AST/ast-dump-traits.cpp
index 1826268272545..8fdb1eedcde39 100644
--- a/clang/test/AST/ast-dump-traits.cpp
+++ b/clang/test/AST/ast-dump-traits.cpp
@@ -95,8 +95,8 @@ constexpr std::strong_ordering test_strong_ordering_type_trait() {
 // CHECK-NEXT:   |-TemplateTypeParmDecl {{.*}} <col:20, col:26> col:26{{( imported)?}} referenced class depth 0 index 1 U
 // CHECK-NEXT:   `-FunctionDecl {{.*}} <line:54:1, line:56:1> line:54:32{{( imported)?}} constexpr test_strong_ordering_type_trait 'std::strong_ordering ()'
 // CHECK-NEXT:     `-CompoundStmt {{.*}} <col:66, line:56:1>
-// CHECK-NEXT:       `-ReturnStmt {{.*}} <line:55:3, col:36>
-// CHECK-NEXT:         `-TypeTraitExpr {{.*}} <col:10, col:36> 'std::strong_ordering' __builtin_type_order
+// CHECK-NEXT:       `-ReturnStmt {{.*}} <line:55:3, col:35>
+// CHECK-NEXT:         `-TypeTraitExpr {{.*}} <col:10, col:35> 'std::strong_ordering' __builtin_type_order
 // CHECK-NEXT:           |-TemplateTypeParmType {{.*}} 'T' dependent{{( imported)?}} depth 0 index 0
 // CHECK-NEXT:           | `-TemplateTypeParm {{.*}} 'T'
 // CHECK-NEXT:           `-TemplateTypeParmType {{.*}} 'U' dependent{{( imported)?}} depth 0 index 1

>From 9d56f6f9841d39751ec2e235454bb056b8a914f6 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <mfwippich at gmail.com>
Date: Wed, 19 Aug 2026 05:08:38 +0200
Subject: [PATCH 8/8] revert accidental formatting change

---
 clang/lib/Sema/TreeTransform.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 454e497c385fe..e6abb3ad577c6 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -15486,7 +15486,8 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr(UnresolvedLookupExpr *Old,
 }
 
 template<typename Derived>
-ExprResult TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
+ExprResult
+TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
   bool ArgChanged = false;
   SmallVector<TypeSourceInfo *, 4> Args;
   for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I) {



More information about the cfe-commits mailing list