Author: Matthias Wippich
Date: 2026-08-20T13:51:36+02:00
New Revision: 0085ac9eb8e33b5f44f84b4015621faaafa1a1bd

URL: 
https://github.com/llvm/llvm-project/commit/0085ac9eb8e33b5f44f84b4015621faaafa1a1bd
DIFF: 
https://github.com/llvm/llvm-project/commit/0085ac9eb8e33b5f44f84b4015621faaafa1a1bd.diff

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

This patch implements a __builtin_type_order intrinsic to support the
implementation of
[P2830R10](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2830r10.html)
Constexpr Type Ordering.

The `__builtin_type_order` builtin returns a `std::strong_ordering` to
match GCC's behavior. Similar to GCC, we establish a total order over
all types by doing lexicographical comparisons over the mangled type
names. This may yield different orderings with different ABIs.

Resolves https://github.com/llvm/llvm-project/issues/146838

Added: 
    clang/test/SemaCXX/builtin-type-order.cpp

Modified: 
    clang/docs/LanguageExtensions.md
    clang/docs/ReleaseNotes.md
    clang/include/clang/AST/ExprCXX.h
    clang/include/clang/AST/Stmt.h
    clang/include/clang/Basic/BuiltinTraits.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/AST/ByteCode/Compiler.cpp
    clang/lib/AST/ExprCXX.cpp
    clang/lib/AST/ExprConstant.cpp
    clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
    clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
    clang/lib/CodeGen/CGExprAgg.cpp
    clang/lib/CodeGen/CGExprScalar.cpp
    clang/lib/Sema/SemaTypeTraits.cpp
    clang/lib/Serialization/ASTReaderStmt.cpp
    clang/lib/Serialization/ASTWriterStmt.cpp
    clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
    clang/test/AST/ast-dump-traits.cpp
    clang/test/CIR/CodeGen/cxx-traits.cpp
    clang/test/PCH/cxx-traits.cpp
    clang/test/PCH/cxx-traits.h

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.md 
b/clang/docs/LanguageExtensions.md
index 3fbdbd9a511f3..5e7f147f23259 100644
--- a/clang/docs/LanguageExtensions.md
+++ b/clang/docs/LanguageExtensions.md
@@ -2059,6 +2059,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 6c23f31cd8f87..3d28f1d4407a5 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -138,6 +138,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/ExprCXX.h 
b/clang/include/clang/AST/ExprCXX.h
index 59a70942af4e1..bb790b1100e7f 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"
@@ -2905,7 +2906,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);
 
@@ -2934,6 +2935,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);
@@ -2947,6 +2954,10 @@ class TypeTraitExpr final
     return TypeTraitExprBits.IsBooleanTypeTrait;
   }
 
+  bool isStoredAsComparisonResult() const {
+    return TypeTraitExprBits.IsComparisonResult;
+  }
+
   bool getBoolValue() const {
     assert(!isValueDependent() && TypeTraitExprBits.IsBooleanTypeTrait);
     return TypeTraitExprBits.Value;

diff  --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 69db8252f931e..5d27ded64082d 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -996,6 +996,9 @@ class alignas(void *) Stmt {
     LLVM_PREFERRED_TYPE(bool)
     unsigned IsBooleanTypeTrait : 1;
 
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned IsComparisonResult : 1;
+
     /// If this expression is a non value-dependent boolean trait,
     /// this indicates whether the trait evaluated true or false.
     LLVM_PREFERRED_TYPE(bool)

diff  --git a/clang/include/clang/Basic/BuiltinTraits.td 
b/clang/include/clang/Basic/BuiltinTraits.td
index f5546acdb17c4..f80234dd21e14 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_order";
+  let KeyFlags = [KEYCXX20];
+}
+
 // MS Extensions
 def IsInterfaceClass : UnaryTrait {
   let Spelling = "__is_interface_class";

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d7f25cf109523..3a910c9c3f2b9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13358,7 +13358,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<=>'|compute 
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/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 52b45e70b08ff..69df4ecf6825a 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,
+    /// 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 cbfd067bfbdd4..9143de1548d7f 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->isStoredAsComparisonResult()) {
+    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 08c931d863044..9d42d47f24840 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"
@@ -1889,10 +1890,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!");
@@ -1902,11 +1903,18 @@ TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation 
Loc, TypeTrait Kind,
          "TypeTraitExprBits.Kind overflow!");
 
   TypeTraitExprBits.IsBooleanTypeTrait = std::holds_alternative<bool>(Value);
+  TypeTraitExprBits.IsComparisonResult =
+      std::holds_alternative<ComparisonCategoryResult>(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 &&
@@ -1948,6 +1956,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 7aff17100d4ff..348e8025fd9fd 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"
@@ -11358,6 +11359,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);
@@ -19511,6 +19513,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;
@@ -19533,24 +19551,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->isStoredAsComparisonResult() &&
+         "expected a strong_ordering 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..922c4cb65e66e 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->isStoredAsComparisonResult() &&
+           "expected a strong_ordering 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 65812318cc1f9..fcc807dfc64cf 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..0c58c4b06b42e 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->isStoredAsComparisonResult() &&
+         "expected a strong_ordering 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 1655c0e93d312..173931213ce68 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 53ab235f3654a..351992873f236 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"
@@ -631,6 +633,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) {
@@ -1408,6 +1430,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;
@@ -1466,11 +1514,14 @@ bool Sema::CheckTypeTraitArity(unsigned Arity, 
SourceLocation Loc, size_t N) {
 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;
 }
 
@@ -1507,6 +1558,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/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index c2d8267b6fcea..92c555dc427b3 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2239,6 +2239,7 @@ void 
ASTStmtReader::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
 void ASTStmtReader::VisitTypeTraitExpr(TypeTraitExpr *E) {
   VisitExpr(E);
   E->TypeTraitExprBits.IsBooleanTypeTrait = Record.readInt();
+  E->TypeTraitExprBits.IsComparisonResult = Record.readInt();
   E->TypeTraitExprBits.NumArgs = Record.readInt();
   E->TypeTraitExprBits.Kind = Record.readInt();
 
@@ -4471,7 +4472,7 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
     case EXPR_TYPE_TRAIT:
       S = TypeTraitExpr::CreateDeserialized(
           Context, Record[ASTStmtReader::NumExprFields],
-          Record[ASTStmtReader::NumExprFields + 1]);
+          Record[ASTStmtReader::NumExprFields + 2]);
       break;
 
     case EXPR_ARRAY_TYPE_TRAIT:

diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index bf90b247fd6ab..782fecdbd0c80 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2256,11 +2256,14 @@ void 
ASTStmtWriter::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
 void ASTStmtWriter::VisitTypeTraitExpr(TypeTraitExpr *E) {
   VisitExpr(E);
   Record.push_back(E->TypeTraitExprBits.IsBooleanTypeTrait);
+  Record.push_back(E->TypeTraitExprBits.IsComparisonResult);
   Record.push_back(E->TypeTraitExprBits.NumArgs);
   Record.push_back(E->TypeTraitExprBits.Kind); // FIXME: Stable encoding
 
   if (E->TypeTraitExprBits.IsBooleanTypeTrait)
     Record.push_back(E->TypeTraitExprBits.Value);
+  else if (E->isValueDependent())
+    Record.AddAPValue(APValue());
   else
     Record.AddAPValue(E->getAPValue());
 

diff  --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp 
b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 57c97b2852445..55669c1bef5d1 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->isStoredAsComparisonResult())
+      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..ca84c7db28145 100644
--- a/clang/test/AST/ast-dump-traits.cpp
+++ b/clang/test/AST/ast-dump-traits.cpp
@@ -1,10 +1,10 @@
 // Test without serialization:
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ast-dump %s \
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++20 -ast-dump %s \
 // RUN: | FileCheck -strict-whitespace %s
 //
 // Test with serialization:
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-pch -o %t %s
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown -x c++ -include-pch %t 
-ast-dump-all /dev/null \
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++20 -emit-pch -o %t %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++20 -x c++ 
-include-pch %t -ast-dump-all /dev/null \
 // RUN: | FileCheck -strict-whitespace %s
 
 void test_type_trait() {
@@ -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: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
+// 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..20a1d5b576db8 100644
--- a/clang/test/PCH/cxx-traits.cpp
+++ b/clang/test/PCH/cxx-traits.cpp
@@ -4,6 +4,10 @@
 // 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 -x c++-header -std=c++20 -emit-pch -o %t 
%S/cxx-traits.h
+// RUN: %clang_cc1 -fms-extensions -std=c++20 -include-pch %t -DPCH 
-fsyntax-only -verify %s
+// RUN: %clang_cc1 -fms-extensions -std=c++20 -include-pch %t -DPCH 
-fsyntax-only -verify -fexperimental-new-constant-interpreter %s
+
 #ifdef PCH
 // expected-no-diagnostics
 #endif
@@ -66,3 +70,8 @@ bool _is_union_result = __is_union(int);
 bool _is_unsigned_result = __is_unsigned(int);
 bool _is_void_result = __is_void(int);
 bool _is_volatile_result = __is_volatile(int);
+
+#if __cplusplus >= 202002L
+static_assert(type_order<int, int>().value == std::__order::equal, "");
+static_assert(type_order<int, long>().value != std::__order::equal, "");
+#endif

diff  --git a/clang/test/PCH/cxx-traits.h b/clang/test/PCH/cxx-traits.h
index 0a4bd09c363e9..54c86d60e3e48 100644
--- a/clang/test/PCH/cxx-traits.h
+++ b/clang/test/PCH/cxx-traits.h
@@ -69,3 +69,28 @@ struct __is_volatile {};  // expected-warning {{made 
available}}
 
 
 }
+
+#if __cplusplus >= 202002L
+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);
+}
+#endif

diff  --git a/clang/test/SemaCXX/builtin-type-order.cpp 
b/clang/test/SemaCXX/builtin-type-order.cpp
new file mode 100644
index 0000000000000..42c8c16bfbace
--- /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@-1 {{cannot compute 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>());


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to