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
