[clang] [Clang][C++23] Core language changes from P1467R9 extended floating-point types and standard names. (PR #78503)

M. Zeeshan Siddiqui via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 17 20:52:46 PST 2024


https://github.com/codemzs updated https://github.com/llvm/llvm-project/pull/78503

>From c9a95ed6db27861de2e7363eff17839b3bcb32d1 Mon Sep 17 00:00:00 2001
From: "M. Zeeshan Siddiqui" <mzs at microsoft.com>
Date: Mon, 13 Nov 2023 17:37:36 +0000
Subject: [PATCH] [Clang][C++23] Implement core language changes from P1467R9
 extended floating-point types and standard names.

This commit implements Core language changes based on P1467R9 Extended
floating-point types and standard names.

As per the proposal's definition the following two types are marked as
extended floating point: Float16 (aka _Float16) and
Bfloat16 (aka decltype (0.0bf16) or __bf16). Future work can extend this
to support other floating-point types such as Float32, Float64, and
Float128.

RFC: https://discourse.llvm.org/t/rfc-c-23-p1467r9-extended-floating-point-types-and-standard-names/70033

Differential Revision: https://reviews.llvm.org/D149573
---
 clang/docs/ReleaseNotes.rst                   |   5 +
 clang/include/clang/AST/ASTContext.h          |  54 +-
 clang/include/clang/AST/BuiltinTypes.def      |   4 +-
 clang/include/clang/AST/Type.h                |   7 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +
 clang/include/clang/Lex/LiteralSupport.h      |   1 +
 .../Core/PathSensitive/SMTConv.h              |  19 +-
 clang/lib/AST/ASTContext.cpp                  | 148 ++++-
 clang/lib/AST/StmtPrinter.cpp                 |   1 +
 clang/lib/AST/Type.cpp                        |  27 +
 clang/lib/Frontend/InitPreprocessor.cpp       |   6 +-
 clang/lib/Lex/LiteralSupport.cpp              |  17 +
 clang/lib/Sema/Sema.cpp                       |  16 +
 clang/lib/Sema/SemaCast.cpp                   |  13 +
 clang/lib/Sema/SemaChecking.cpp               |  23 +-
 clang/lib/Sema/SemaExpr.cpp                   |  60 ++-
 clang/lib/Sema/SemaOverload.cpp               |  81 ++-
 .../cxx23-fp-ext-std-names-p1467r9.cpp        | 499 +++++++++++++++++
 .../test/CodeGenCXX/cxx23-vector-bfloat16.cpp |  67 +++
 .../Sema/cxx23-fp-ext-std-names-p1467r9.cpp   | 505 ++++++++++++++++++
 20 files changed, 1509 insertions(+), 46 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp
 create mode 100644 clang/test/CodeGenCXX/cxx23-vector-bfloat16.cpp
 create mode 100644 clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cf52c1e66b91b4..64cbb55ca80edb 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -192,6 +192,11 @@ C++23 Feature Support
 - Added a separate warning to warn the use of attributes on lambdas as a C++23 extension
   in previous language versions: ``-Wc++23-lambda-attributes``.
 
+- Implemented `P1467R9: Extended floating-point types and standard names <https://wg21.link/P1467R9>`_. Enables
+  extended floating-point types beyond the three standard ones, establishing rules for their interactions and
+  conversions with each other and other types, while ensuring no alteration in the behavior of existing
+  standard types. The current implementation enables support for `std::float16_t` and `std::bfloat16_t` types.
+
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 3e46a5da3fc043..7f823d9e57d3da 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -52,6 +52,16 @@ template <typename T, unsigned N> class SmallPtrSet;
 
 namespace clang {
 
+// Conversion ranks introduced in C++23 6.8.6p2 [conv.rank]
+enum FloatConvRankCompareResult {
+  FRCR_Unordered,
+  FRCR_Lesser,
+  FRCR_Greater,
+  FRCR_Equal,
+  FRCR_Equal_Lesser_Subrank,
+  FRCR_Equal_Greater_Subrank,
+};
+
 class APValue;
 class ASTMutationListener;
 class ASTRecordLayout;
@@ -1105,8 +1115,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   CanQualType SatUnsignedShortFractTy, SatUnsignedFractTy,
       SatUnsignedLongFractTy;
   CanQualType HalfTy; // [OpenCL 6.1.1.1], ARM NEON
-  CanQualType BFloat16Ty;
-  CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3
+  CanQualType BFloat16Ty; // [C++23 6.8.3p5][basic.extended.fp]
+  CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3 and [C++23 6.8.3p1][basic.extended.fp]
   CanQualType VoidPtrTy, NullPtrTy;
   CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy;
   CanQualType BuiltinFnTy;
@@ -2388,6 +2398,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// More type predicates useful for type checking/promotion
   bool isPromotableIntegerType(QualType T) const; // C99 6.3.1.1p2
 
+  /// Determine if a floating type can be promoted to another floating type.
+  bool isPromotableFloatingType(QualType T) const;
+
   /// Return the "preferred" alignment of the specified type \p T for
   /// the current target, in bits.
   ///
@@ -2783,6 +2796,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// 6.3.1.1p2, assuming that \p PromotableType is a promotable integer type.
   QualType getPromotedIntegerType(QualType PromotableType) const;
 
+  /// Determines the promoted floating point type.
+  QualType getPromotedFloatingType(QualType PromotableType) const;
+
   /// Recurses in pointer/array types until it finds an Objective-C
   /// retainable type and returns its ownership.
   Qualifiers::ObjCLifetime getInnerObjCOwnership(QualType T) const;
@@ -2803,14 +2819,38 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// Compare the rank of the two specified floating point types,
   /// ignoring the domain of the type (i.e. 'double' == '_Complex double').
   ///
-  /// If \p LHS > \p RHS, returns 1.  If \p LHS == \p RHS, returns 0.  If
-  /// \p LHS < \p RHS, return -1.
-  int getFloatingTypeOrder(QualType LHS, QualType RHS) const;
+  /// If \p LHS > \p RHS, returns FRCR_Greater. If \p LHS == \p RHS, returns
+  /// FRCR_Equal.  If \p LHS < \p RHS, return FRCR_Lesser. If \p LHS and \p RHS
+  /// are unordered, return FRCR_Unordered. If \p LHS and \p RHS are equal but
+  /// the subrank of \p LHS is greater than \p RHS, return
+  /// FRCR_Equal_Greater_Subrank. If \p LHS and \p RHS are equal but the subrank
+  /// of \p LHS is less than \p RHS, return FRCR_Equal_Lesser_Subrank. Subrank
+  /// and Unordered comparison were introduced in C++23.
+  FloatConvRankCompareResult getFloatingTypeOrder(QualType LHS,
+                                                 QualType RHS) const;
 
   /// Compare the rank of two floating point types as above, but compare equal
   /// if both types have the same floating-point semantics on the target (i.e.
-  /// long double and double on AArch64 will return 0).
-  int getFloatingTypeSemanticOrder(QualType LHS, QualType RHS) const;
+  /// long double and double on AArch64 will return FRCR_Equal).
+  FloatConvRankCompareResult getFloatingTypeSemanticOrder(QualType LHS,
+                                                         QualType RHS) const;
+
+  /// C++23 6.8.2p12 [basic.fundamental]
+  /// Checks if extended floating point rules apply to a pair of types.
+  /// It returns true if both the types are C++23 floating point types and
+  /// at least one of them is a C++23 extended floating point type. It returns
+  /// false for pairs of standard C++23 floating point types.
+  bool doCXX23ExtendedFpTypesRulesApply(QualType T1, QualType T2) const;
+
+  /// C++23 6.8.2p12 [basic.fundamental]
+  /// Returns true if \p Result is FRCR_Lesser or FRCR_Unordered rank.
+  bool
+  isCXX23SmallerOrUnorderedFloatingPointRank(FloatConvRankCompareResult Result) const;
+
+  /// C++23 6.8.2p12 [basic.fundamental]
+  /// Returns true if \p Result is FRCR_Equal, FRCR_Equal_Lesser_Subrank or
+  /// FRCR_Equal_Greater_Subrank.
+  bool isCXX23EqualFloatingPointRank(FloatConvRankCompareResult Result) const;
 
   unsigned getTargetAddressSpace(LangAS AS) const;
 
diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def
index c04f6f6f127191..0cb1bfacb71161 100644
--- a/clang/include/clang/AST/BuiltinTypes.def
+++ b/clang/include/clang/AST/BuiltinTypes.def
@@ -209,10 +209,10 @@ FLOATING_TYPE(Double, DoubleTy)
 // 'long double'
 FLOATING_TYPE(LongDouble, LongDoubleTy)
 
-// '_Float16'
+// '_Float16', 'std::float16_t'
 FLOATING_TYPE(Float16, HalfTy)
 
-// '__bf16'
+// '__bf16', 'std::bfloat16_t'
 FLOATING_TYPE(BFloat16, BFloat16Ty)
 
 // '__float128'
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index a7efe78591635e..5dc3ac9e3c7498 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2242,6 +2242,13 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isComplexType() const;      // C99 6.2.5p11 (complex)
   bool isAnyComplexType() const;   // C99 6.2.5p11 (complex) + Complex Int.
   bool isFloatingType() const;     // C99 6.2.5p11 (real floating + complex)
+  /// C++23 6.8.2p12 [basic.fundamental] (standard floating point + extended
+  /// floating point)
+  bool isCXX23FloatingPointType(const ASTContext &Ctx) const;
+  /// C++23 6.8.2p12 [basic.fundamental] (standard floating point)
+  bool isCXX23StandardFloatingPointType(const ASTContext &Ctx) const;
+  /// C++23 6.8.2p12 [basic.fundamental] (extended floating point)
+  bool isCXX23ExtendedFloatingPointType(const ASTContext &Ctx) const;
   bool isHalfType() const;         // OpenCL 6.1.1.1, NEON (IEEE 754-2008 half)
   bool isFloat16Type() const;      // C11 extension ISO/IEC TS 18661
   bool isBFloat16Type() const;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 03b0122d1c08f7..7b83978a7eb9c7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8982,6 +8982,8 @@ def err_cast_pointer_to_non_pointer_int : Error<
 def err_nullptr_cast : Error<
   "cannot cast an object of type %select{'nullptr_t' to %1|%1 to 'nullptr_t'}0"
 >;
+def err_invalid_implicit_floating_point_cast : Error<
+  "floating-point type %0 cannot be implicitly converted to type %1">;
 def err_typecheck_expect_scalar_operand : Error<
   "operand of type %0 where arithmetic or pointer type is required">;
 def err_typecheck_cond_incompatible_operands : Error<
diff --git a/clang/include/clang/Lex/LiteralSupport.h b/clang/include/clang/Lex/LiteralSupport.h
index 643ddbdad8c87d..c1301e56af97f3 100644
--- a/clang/include/clang/Lex/LiteralSupport.h
+++ b/clang/include/clang/Lex/LiteralSupport.h
@@ -81,6 +81,7 @@ class NumericLiteralParser {
   bool isFract : 1;         // 1.0hr/r/lr/uhr/ur/ulr
   bool isAccum : 1;         // 1.0hk/k/lk/uhk/uk/ulk
   bool isBitInt : 1;        // 1wb, 1uwb (C23)
+  bool isBFloat16 : 1;      // 1.0bf
   uint8_t MicrosoftInteger; // Microsoft suffix extension i8, i16, i32, or i64.
 
 
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
index fcc9c02999b3b0..c7eb76d14a295d 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H
 #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H
 
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/Expr.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
@@ -774,16 +775,24 @@ class SMTConv {
 
     // If we have two real floating types, convert the smaller operand to the
     // bigger result
+    // FIXME: It isn't clear how unordered and equal floating-point ranks should
+    // FIXME: be handled here. For now, they are arbitrarily handled by adding a
+    // FIXME: cast to the RHS to match the LHS.
     // Note: Safe to skip updating bitwidth because this must terminate
-    int order = Ctx.getFloatingTypeOrder(LTy, RTy);
-    if (order > 0) {
+    FloatConvRankCompareResult order = Ctx.getFloatingTypeOrder(LTy, RTy);
+    switch (order) {
+    case FRCR_Unordered:
+    case FRCR_Greater:
+    case FRCR_Equal:
+    case FRCR_Equal_Greater_Subrank:
       RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
       RTy = LTy;
-    } else if (order == 0) {
+      break;
+    case FRCR_Lesser:
+    case FRCR_Equal_Lesser_Subrank:
       LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
       LTy = RTy;
-    } else {
-      llvm_unreachable("Unsupported floating-point type cast!");
+      break;
     }
   }
 };
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index d9cefcaa84d7e5..8afcff25f4ee44 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -112,6 +112,85 @@ enum FloatingRank {
   Ibm128Rank
 };
 
+constexpr unsigned CXX23FloatRankToIndex(clang::BuiltinType::Kind Kind) {
+  switch (Kind) {
+  case clang::BuiltinType::Float16:
+    return 0;
+  case clang::BuiltinType::BFloat16:
+    return 1;
+  case clang::BuiltinType::Float:
+    return 2;
+  case clang::BuiltinType::Double:
+    return 3;
+  case clang::BuiltinType::LongDouble:
+    return 4;
+  default:
+    // Both __float128 and __ibm128 are compiler extensions, not extended floating points.
+    // __float128 also predates the invention of floating-point types.
+    llvm_unreachable("Not a CXX23+ floating point builtin type");
+  }
+}
+
+// C++23 6.8.6p2 [conv.rank]
+// Grid to determine the rank of a floating point type when compared with
+// another floating point type.
+constexpr std::array<std::array<FloatConvRankCompareResult, 5>, 5>
+    CXX23FloatingPointConversionRankMap = {
+        {// Float16 x Float16
+         // Float16 x BFloat16
+         // Float16 x Float
+         // Float16 x Double
+         // Float16 x LongDouble
+         {{FloatConvRankCompareResult::FRCR_Equal,
+           FloatConvRankCompareResult::FRCR_Unordered,
+           FloatConvRankCompareResult::FRCR_Lesser,
+           FloatConvRankCompareResult::FRCR_Lesser,
+           FloatConvRankCompareResult::FRCR_Lesser}},
+
+         // BFloat16 x Float16
+         // BFloat16 x BFloat16
+         // BFloat16 x Float
+         // BFloat16 x Double
+         // BFloat16 x LongDouble
+         {{FloatConvRankCompareResult::FRCR_Unordered,
+           FloatConvRankCompareResult::FRCR_Equal,
+           FloatConvRankCompareResult::FRCR_Lesser,
+           FloatConvRankCompareResult::FRCR_Lesser,
+           FloatConvRankCompareResult::FRCR_Lesser}},
+
+         // Float x Float16
+         // Float x BFloat16
+         // Float x Float
+         // Float x Double
+         // Float x LongDouble
+         {{FloatConvRankCompareResult::FRCR_Greater,
+           FloatConvRankCompareResult::FRCR_Greater,
+           FloatConvRankCompareResult::FRCR_Equal,
+           FloatConvRankCompareResult::FRCR_Lesser,
+           FloatConvRankCompareResult::FRCR_Lesser}},
+
+         // Double x Float16
+         // Double x BFloat16
+         // Double x Float
+         // Double x Double
+         // Double x LongDouble
+         {{FloatConvRankCompareResult::FRCR_Greater,
+           FloatConvRankCompareResult::FRCR_Greater,
+           FloatConvRankCompareResult::FRCR_Greater,
+           FloatConvRankCompareResult::FRCR_Equal,
+           FloatConvRankCompareResult::FRCR_Lesser}},
+
+         // LongDouble x Float16
+         // LongDouble x BFloat16
+         // LongDouble x Float
+         // LongDouble x Double
+         // LongDouble x LongDouble
+         {{FloatConvRankCompareResult::FRCR_Greater,
+           FloatConvRankCompareResult::FRCR_Greater,
+           FloatConvRankCompareResult::FRCR_Greater,
+           FloatConvRankCompareResult::FRCR_Greater,
+           FloatConvRankCompareResult::FRCR_Equal}}}};
+
 /// \returns The locations that are relevant when searching for Doc comments
 /// related to \p D.
 static SmallVector<SourceLocation, 2>
@@ -1824,6 +1903,10 @@ bool ASTContext::isPromotableIntegerType(QualType T) const {
   return false;
 }
 
+bool ASTContext::isPromotableFloatingType(QualType T) const {
+  return T->getAs<BuiltinType>()->getKind() == BuiltinType::Float;
+}
+
 bool ASTContext::isAlignmentRequired(const Type *T) const {
   return getTypeInfo(T).AlignRequirement != AlignRequirementKind::None;
 }
@@ -7007,27 +7090,69 @@ static FloatingRank getFloatingRank(QualType T) {
   }
 }
 
+/// C++23 6.8.5 [conv.rank]
 /// getFloatingTypeOrder - Compare the rank of the two specified floating
 /// point types, ignoring the domain of the type (i.e. 'double' ==
-/// '_Complex double').  If LHS > RHS, return 1.  If LHS == RHS, return 0. If
-/// LHS < RHS, return -1.
-int ASTContext::getFloatingTypeOrder(QualType LHS, QualType RHS) const {
+/// '_Complex double').
+/// If LHS > RHS, return FRCR_Greater.  If LHS == RHS, return FRCR_Equal. If
+/// LHS < RHS, return FRCR_Lesser. If the values representedable by the two
+/// are not subset of each other, return FRCR_Unordered. If LHS == RHS but
+/// LHS has a higher subrank than RHS return FRCR_Equal_Greater_Subrank else
+/// return FRCR_Equal_Lesser_Subrank.
+FloatConvRankCompareResult
+ASTContext::getFloatingTypeOrder(QualType LHS, QualType RHS) const {
+  if (LHS->isCXX23FloatingPointType(*this) &&
+      RHS->isCXX23FloatingPointType(*this)) {
+    BuiltinType::Kind LHSKind;
+    BuiltinType::Kind RHSKind;
+    if (const auto *CT = LHS->getAs<ComplexType>())
+      LHSKind = CT->getElementType()->castAs<BuiltinType>()->getKind();
+    else
+      LHSKind = LHS->castAs<BuiltinType>()->getKind();
+    if (const auto *CT = RHS->getAs<ComplexType>())
+      RHSKind = CT->getElementType()->castAs<BuiltinType>()->getKind();
+    else
+      RHSKind = RHS->castAs<BuiltinType>()->getKind();
+    return CXX23FloatingPointConversionRankMap[CXX23FloatRankToIndex(LHSKind)]
+                                              [CXX23FloatRankToIndex(RHSKind)];
+  }
+
   FloatingRank LHSR = getFloatingRank(LHS);
   FloatingRank RHSR = getFloatingRank(RHS);
 
   if (LHSR == RHSR)
-    return 0;
+    return FRCR_Equal;
   if (LHSR > RHSR)
-    return 1;
-  return -1;
+    return FRCR_Greater;
+  return FRCR_Lesser;
 }
 
-int ASTContext::getFloatingTypeSemanticOrder(QualType LHS, QualType RHS) const {
+FloatConvRankCompareResult
+ASTContext::getFloatingTypeSemanticOrder(QualType LHS, QualType RHS) const {
   if (&getFloatTypeSemantics(LHS) == &getFloatTypeSemantics(RHS))
-    return 0;
+    return FRCR_Equal;
   return getFloatingTypeOrder(LHS, RHS);
 }
 
+bool ASTContext::doCXX23ExtendedFpTypesRulesApply(QualType T1,
+                                                  QualType T2) const {
+  return (((T1->isCXX23FloatingPointType(*this) &&
+            T2->isCXX23FloatingPointType(*this))) &&
+          (T1->isCXX23ExtendedFloatingPointType(*this) ||
+           T2->isCXX23ExtendedFloatingPointType(*this)));
+}
+
+bool ASTContext::isCXX23SmallerOrUnorderedFloatingPointRank(
+    FloatConvRankCompareResult Result) const {
+  return (Result == FRCR_Lesser) || (Result == FRCR_Unordered);
+}
+
+bool ASTContext::isCXX23EqualFloatingPointRank(
+    FloatConvRankCompareResult Result) const {
+  return (Result == FRCR_Equal) || (Result == FRCR_Equal_Greater_Subrank) ||
+         (Result == FRCR_Equal_Lesser_Subrank);
+}
+
 /// getIntegerRank - Return an integer conversion rank (C99 6.3.1.1p1). This
 /// routine will assert if passed a built-in type that isn't an integer or enum,
 /// or if it is not canonicalized.
@@ -7182,6 +7307,13 @@ QualType ASTContext::getPromotedIntegerType(QualType Promotable) const {
   return (PromotableSize != IntSize) ? IntTy : UnsignedIntTy;
 }
 
+QualType ASTContext::getPromotedFloatingType(QualType Promotable) const {
+  assert(!Promotable.isNull());
+  assert(isPromotableFloatingType(Promotable));
+  assert(Promotable->getAs<BuiltinType>()->getKind() == BuiltinType::Float);
+  return DoubleTy;
+}
+
 /// Recurses in pointer/array types until it finds an objc retainable
 /// type and returns its ownership.
 Qualifiers::ObjCLifetime ASTContext::getInnerObjCOwnership(QualType T) const {
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index c04cb313c3387a..b762b581857abf 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1358,6 +1358,7 @@ static void PrintFloatingLiteral(raw_ostream &OS, FloatingLiteral *Node,
   case BuiltinType::Float:      OS << 'F'; break;
   case BuiltinType::LongDouble: OS << 'L'; break;
   case BuiltinType::Float128:   OS << 'Q'; break;
+  case BuiltinType::BFloat16:   OS << "BF16"; break;
   }
 }
 
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index b419fc8836b032..d15b04e46a9c5c 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2192,6 +2192,33 @@ bool Type::isFloatingType() const {
   return false;
 }
 
+bool Type::isCXX23StandardFloatingPointType(const ASTContext &Ctx) const {
+  if (!Ctx.getLangOpts().CPlusPlus23)
+    return false;
+  if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
+    return BT->getKind() >= BuiltinType::Float &&
+           BT->getKind() <= BuiltinType::LongDouble;
+  if (const auto *CT = dyn_cast<ComplexType>(CanonicalType))
+    return CT->getElementType()->isCXX23StandardFloatingPointType(Ctx);
+  return false;
+}
+
+bool Type::isCXX23ExtendedFloatingPointType(const ASTContext &Ctx) const {
+  if (!Ctx.getLangOpts().CPlusPlus23)
+    return false;
+  if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
+    return BT->getKind() == BuiltinType::Float16 ||
+           BT->getKind() == BuiltinType::BFloat16;
+  if (const auto *CT = dyn_cast<ComplexType>(CanonicalType))
+    return CT->getElementType()->isCXX23ExtendedFloatingPointType(Ctx);
+  return false;
+}
+
+bool Type::isCXX23FloatingPointType(const ASTContext &Ctx) const {
+  return isCXX23StandardFloatingPointType(Ctx) ||
+         isCXX23ExtendedFloatingPointType(Ctx);
+}
+
 bool Type::hasFloatingRepresentation() const {
   if (const auto *VT = dyn_cast<VectorType>(CanonicalType))
     return VT->getElementType()->isFloatingType();
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index d83128adb511ef..f22ba2f5eae8f2 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -453,8 +453,12 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI,
     if (LangOpts.CPlusPlus26)
       // FIXME: Use correct value for C++26.
       Builder.defineMacro("__cplusplus", "202400L");
-    else if (LangOpts.CPlusPlus23)
+    else if (LangOpts.CPlusPlus23) {
       Builder.defineMacro("__cplusplus", "202302L");
+      // [C++23] 15.11p2 [cpp.predefined]
+      Builder.defineMacro("__STDCPP_FLOAT16_T__", "1");
+      Builder.defineMacro("__STDCPP_BFLOAT16_T__", "1");
+    }
     //      [C++20] The integer literal 202002L.
     else if (LangOpts.CPlusPlus20)
       Builder.defineMacro("__cplusplus", "202002L");
diff --git a/clang/lib/Lex/LiteralSupport.cpp b/clang/lib/Lex/LiteralSupport.cpp
index 0a78638f680511..21734c2c19fb54 100644
--- a/clang/lib/Lex/LiteralSupport.cpp
+++ b/clang/lib/Lex/LiteralSupport.cpp
@@ -925,6 +925,7 @@ NumericLiteralParser::NumericLiteralParser(StringRef TokSpelling,
   isAccum = false;
   hadError = false;
   isBitInt = false;
+  isBFloat16 = false;
 
   // This routine assumes that the range begin/end matches the regex for integer
   // and FP constants (specifically, the 'pp-number' regex), and assumes that
@@ -1029,6 +1030,21 @@ NumericLiteralParser::NumericLiteralParser(StringRef TokSpelling,
 
       isFloat = true;
       continue;  // Success.
+    // C++23 5.13.4 [lex.fcon]
+    case 'b':
+    case 'B':
+      if (!isFPConstant)
+        break; // Error for integer constant.
+      if (s + 3 < ThisTokEnd && (s[1] == 'f' || s[1] == 'F') && s[2] == '1' &&
+          s[3] == '6') {
+        if (HasSize)
+          break;
+        HasSize = true;
+        s += 3;
+        isBFloat16 = true;
+        continue;
+      }
+      break;
     case 'q':    // FP Suffix for "__float128"
     case 'Q':
       if (!isFPConstant) break;  // Error for integer constant.
@@ -1161,6 +1177,7 @@ NumericLiteralParser::NumericLiteralParser(StringRef TokSpelling,
         saw_fixed_point_suffix = false;
         isFract = false;
         isAccum = false;
+        isBFloat16 = false;
       }
 
       saw_ud_suffix = true;
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index cafbecebc8a119..fbc0cda8d4e5c5 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -700,6 +700,22 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
     }
   }
 
+  // C++23 7.3.10 [conv.double]
+  // A prvalue of floating-point type can be converted to a prvalue of another
+  // floating-point type with a greater or equal conversion rank ([conv.rank]).
+  // A prvalue of standard floating-point type can be converted to a prvalue of
+  // another standard floating-point type
+  if (Context.doCXX23ExtendedFpTypesRulesApply(ExprTy, TypeTy) &&
+      Kind == CK_FloatingCast && E->isPRValue() &&
+      (CCK == CCK_ImplicitConversion)) {
+    if (Context.isCXX23SmallerOrUnorderedFloatingPointRank(
+            Context.getFloatingTypeOrder(TypeTy, ExprTy))) {
+      Diag(E->getExprLoc(), diag::err_invalid_implicit_floating_point_cast)
+          << TypeTy << ExprTy << E->getSourceRange();
+      return ExprError();
+    }
+  }
+
   if (ImplicitCastExpr *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
     if (ImpCast->getCastKind() == Kind && (!BasePath || BasePath->empty())) {
       ImpCast->setType(Ty);
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 9d85568d97b2d2..c905d11b8af82a 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -1386,6 +1386,19 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr,
     }
   }
 
+  // [expr.static.cast] 7.6.1.9p11, A prvalue of floating-point type can
+  // be explicitly converted to any other floating-point type.
+  // Conversion between fp16 and bf16 is not supported yet.
+  if (SrcExpr.get()->isPRValue() &&
+      Self.Context.doCXX23ExtendedFpTypesRulesApply(DestType, SrcType)) {
+    // FIXME: Support for cast between fp16 and bf16 doesn't exist yet.
+    if (!((DestType->isBFloat16Type() || DestType->isFloat16Type()) &&
+          (SrcType->isBFloat16Type() || SrcType->isFloat16Type()))) {
+      Kind = CK_FloatingCast;
+      return TC_Success;
+    }
+  }
+
   // Reverse integral promotion/conversion. All such conversions are themselves
   // again integral promotions or conversions and are thus already handled by
   // p2 (TryDirectInitialization above).
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index ace3e386988f00..08dbafb7c23a9a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -11960,9 +11960,11 @@ isArithmeticArgumentPromotion(Sema &S, const ImplicitCastExpr *ICE) {
     From = VecTy->getElementType();
   if (const auto *VecTy = To->getAs<ExtVectorType>())
     To = VecTy->getElementType();
-  // It's a floating promotion if the source type is a lower rank.
-  return ICE->getCastKind() == CK_FloatingCast &&
-         S.Context.getFloatingTypeOrder(From, To) < 0;
+  // It's a floating promotion if the source type is float.
+  // [7.3.8p1][conv.fpprom] https://eel.is/c++draft/conv.fpprom
+  return (ICE->getCastKind() == CK_FloatingCast &&
+          S.Context.isPromotableFloatingType(From) &&
+          S.Context.getPromotedFloatingType(From) == To);
 }
 
 bool
@@ -15272,9 +15274,12 @@ static void AnalyzeCompoundAssignment(Sema &S, BinaryOperator *E) {
     return;
 
   // If both source and target are floating points, warn about losing precision.
-  int Order = S.getASTContext().getFloatingTypeSemanticOrder(
-      QualType(ResultBT, 0), QualType(RBT, 0));
-  if (Order < 0 && !S.SourceMgr.isInSystemMacro(E->getOperatorLoc()))
+  FloatConvRankCompareResult Order =
+      S.getASTContext().getFloatingTypeSemanticOrder(QualType(ResultBT, 0),
+                                                     QualType(RBT, 0));
+
+  assert(Order != FRCR_Unordered && "Invalid floating point types");
+  if (Order == FRCR_Lesser && !S.SourceMgr.isInSystemMacro(E->getOperatorLoc()))
     // warn about dropping FP rank.
     DiagnoseImpCast(S, E->getRHS(), E->getLHS()->getType(), E->getOperatorLoc(),
                     diag::warn_impcast_float_result_precision);
@@ -15700,7 +15705,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
 
       int Order = S.getASTContext().getFloatingTypeSemanticOrder(
           QualType(SourceBT, 0), QualType(TargetBT, 0));
-      if (Order > 0) {
+      if (Order == FRCR_Greater) {
         // Don't warn about float constants that are precisely
         // representable in the target type.
         Expr::EvalResult result;
@@ -15718,12 +15723,14 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
         DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_float_precision);
       }
       // ... or possibly if we're increasing rank, too
-      else if (Order < 0) {
+      else if (Order == FRCR_Lesser) {
         if (S.SourceMgr.isInSystemMacro(CC))
           return;
 
         DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_double_promotion);
       }
+      assert(Order != FRCR_Unordered &&
+             "Unordered floating types are not allowed.");
       return;
     }
 
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3df0ec5176743c..8e0c3eb53876c7 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -809,7 +809,7 @@ ExprResult Sema::UsualUnaryConversions(Expr *E) {
       llvm_unreachable("Float evaluation method should be set by now");
       break;
     case LangOptions::FEM_Double:
-      if (Context.getFloatingTypeOrder(Context.DoubleTy, Ty) > 0)
+      if (Context.getFloatingTypeOrder(Context.DoubleTy, Ty) == FRCR_Greater)
         // Widen the expression to double.
         return Ty->isComplexType()
                    ? ImpCastExprToType(E,
@@ -818,7 +818,8 @@ ExprResult Sema::UsualUnaryConversions(Expr *E) {
                    : ImpCastExprToType(E, Context.DoubleTy, CK_FloatingCast);
       break;
     case LangOptions::FEM_Extended:
-      if (Context.getFloatingTypeOrder(Context.LongDoubleTy, Ty) > 0)
+      if (Context.getFloatingTypeOrder(Context.LongDoubleTy, Ty) ==
+          FRCR_Greater)
         // Widen the expression to long double.
         return Ty->isComplexType()
                    ? ImpCastExprToType(
@@ -1169,14 +1170,16 @@ static QualType handleComplexConversion(Sema &S, ExprResult &LHS,
     return RHSType;
 
   // Compute the rank of the two types, regardless of whether they are complex.
-  int Order = S.Context.getFloatingTypeOrder(LHSType, RHSType);
-  if (Order < 0)
+  FloatConvRankCompareResult Order =
+      S.Context.getFloatingTypeOrder(LHSType, RHSType);
+  if (Order == FRCR_Lesser)
     // Promote the precision of the LHS if not an assignment.
     return handleComplexFloatConversion(S, LHS, LHSType, RHSType,
                                         /*PromotePrecision=*/!IsCompAssign);
   // Promote the precision of the RHS unless it is already the same as the LHS.
   return handleComplexFloatConversion(S, RHS, RHSType, LHSType,
-                                      /*PromotePrecision=*/Order > 0);
+                                      /*PromotePrecision=*/Order ==
+                                          clang::FRCR_Greater);
 }
 
 /// Handle arithmetic conversion from integer to float.  Helper function
@@ -1229,16 +1232,34 @@ static QualType handleFloatConversion(Sema &S, ExprResult &LHS,
     return LHSFloat ? LHSType : RHSType;
   }
 
-  // If we have two real floating types, convert the smaller operand
-  // to the bigger result.
+  // C++23 [expr.arith.conv] 7.4
+  // If the floating-point conversion ranks ([conv.rank]) of the types
+  // of the operands are ordered but not equal, then the operand of the type
+  // with the lesser floating-point conversion rank is converted to the type of
+  // the other operand.
+  //
+  // If the floating-point conversion ranks of the types of the operands are
+  // equal, then the operand with the lesser floating-point conversion subrank
+  // ([conv.rank]) is converted to the type of the other operand.
+  //
+  // Otherwise, the expression is ill-formed i.e unordered conversion rank
+  // between floating-point types.
   if (LHSFloat && RHSFloat) {
-    int order = S.Context.getFloatingTypeOrder(LHSType, RHSType);
-    if (order > 0) {
+    FloatConvRankCompareResult order =
+        S.Context.getFloatingTypeOrder(LHSType, RHSType);
+
+    if (order == FRCR_Unordered) {
+      return QualType();
+    }
+
+    if (order == FRCR_Greater || order == FRCR_Equal_Greater_Subrank) {
       RHS = S.ImpCastExprToType(RHS.get(), LHSType, CK_FloatingCast);
       return LHSType;
     }
 
-    assert(order < 0 && "illegal float comparison");
+    assert(((order != FRCR_Equal) &&
+            (order == FRCR_Lesser || order == FRCR_Equal_Lesser_Subrank)) &&
+           "illegal float comparison");
     if (!IsCompAssign)
       LHS = S.ImpCastExprToType(LHS.get(), RHSType, CK_FloatingCast);
     return RHSType;
@@ -4107,6 +4128,8 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
       Ty = Context.Float16Ty;
     else if (Literal.isFloat128)
       Ty = Context.Float128Ty;
+    else if (Literal.isBFloat16)
+      Ty = Context.BFloat16Ty;
     else
       Ty = Context.DoubleTy;
 
@@ -10837,7 +10860,8 @@ static bool tryVectorConvertAndSplat(Sema &S, ExprResult *scalar,
   } else if (vectorEltTy->isRealFloatingType()) {
     if (scalarTy->isRealFloatingType()) {
       if (S.getLangOpts().OpenCL &&
-          S.Context.getFloatingTypeOrder(vectorEltTy, scalarTy) < 0) {
+          S.Context.getFloatingTypeOrder(vectorEltTy, scalarTy) ==
+              clang::FRCR_Lesser) {
         DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type;
         return true;
       }
@@ -11025,8 +11049,18 @@ static bool tryGCCVectorConvertAndSplat(Sema &S, ExprResult *Scalar,
       // expression is instantiated.
       bool CstScalar = Scalar->get()->isValueDependent() ||
                        Scalar->get()->EvaluateAsFloat(Result, S.Context);
-      int Order = S.Context.getFloatingTypeOrder(VectorEltTy, ScalarTy);
-      if (!CstScalar && Order < 0)
+      FloatConvRankCompareResult Order =
+          S.Context.getFloatingTypeOrder(VectorEltTy, ScalarTy);
+
+      // Although the GCC Vector Extensions are not part of the C++23 language
+      // standard, we are currently applying the C++23 extended floating point
+      // rules to them. This is in keeping with the spirit of this function and
+      // maintains consistency in handling floating point types
+      if (S.Context.doCXX23ExtendedFpTypesRulesApply(ScalarTy, VectorEltTy) &&
+          S.Context.isCXX23SmallerOrUnorderedFloatingPointRank(Order))
+        return true;
+
+      if (!CstScalar && (Order == FRCR_Lesser))
         return true;
 
       // If the scalar cannot be safely casted to the vector element type,
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 37c62b306b3cd3..ba9cf196bfe04e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -402,7 +402,7 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
   //    if it cannot be represented exactly), or
   case ICK_Floating_Conversion:
     if (FromType->isRealFloatingType() && ToType->isRealFloatingType() &&
-        Ctx.getFloatingTypeOrder(FromType, ToType) == 1) {
+        Ctx.getFloatingTypeOrder(FromType, ToType) == FRCR_Greater) {
       // FromType is larger than ToType.
       const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
 
@@ -2107,6 +2107,16 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
          (FromType->isFloat16Type() || FromType->isHalfType())))
       return false;
 
+    // C++23 7.3.10p1 [conv.double]
+    // A prvalue of floating-point type can be converted to a prvalue of another
+    // floating-point type with a greater or equal conversion rank
+    // ([conv.rank]). A prvalue of standard floating-point type can be converted
+    // to a prvalue of another standard floating-point type.
+    if (S.Context.doCXX23ExtendedFpTypesRulesApply(FromType, ToType) &&
+        S.Context.isCXX23SmallerOrUnorderedFloatingPointRank(
+            S.Context.getFloatingTypeOrder(ToType, FromType)))
+      return false;
+
     // Conversions between IEEE-quad and IBM-extended semantics are not
     // permitted.
     const llvm::fltSemantics &FromSem =
@@ -4305,6 +4315,73 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc,
                ? ImplicitConversionSequence::Better
                : ImplicitConversionSequence::Worse;
 
+  // C++23 12.2.4.3p4:
+  // A conversion in either direction between floating-point type FP1 and
+  // floating-point type FP2 is better than a conversion in the same direction
+  // between FP1 and arithmetic type T3 if:
+  // 1) The floating-point conversion rank ([conv.rank]) of FP1 is equal to the
+  // rank of FP2, and 2) T3 is not a floating-point type, or T3 is a
+  // floating-point type whose rank is not equal to the rank of FP1, or the
+  // floating-point conversion subrank ([conv.rank]) of FP2 is greater than the
+  // subrank of T3.
+  if (S.Context.getLangOpts().CPlusPlus23) {
+    if (SCS1.getFromType()->isCXX23FloatingPointType(S.Context) &&
+        S.Context.hasSameType(SCS1.getFromType(), SCS2.getFromType())) {
+
+      if ((SCS1.Second == ICK_Floating_Conversion) &&
+          SCS1.getToType(1)->isCXX23FloatingPointType(S.Context) &&
+          S.Context.isCXX23EqualFloatingPointRank(
+              S.Context.getFloatingTypeOrder(SCS1.getToType(1),
+                                             SCS1.getFromType()))) {
+
+        if (SCS2.getToType(1)->isArithmeticType()) {
+          if (!SCS2.getToType(1)->isCXX23FloatingPointType(S.Context)) {
+            return ImplicitConversionSequence::Better;
+          }
+
+          if (!S.Context.isCXX23EqualFloatingPointRank(
+                  S.Context.getFloatingTypeOrder(SCS2.getToType(1),
+                                                 SCS1.getFromType()))) {
+            return ImplicitConversionSequence::Better;
+          }
+
+          if (S.Context.getFloatingTypeOrder(SCS1.getToType(1),
+                                             SCS2.getToType(1)) ==
+              FRCR_Equal_Greater_Subrank) {
+            return ImplicitConversionSequence::Better;
+          }
+          return ImplicitConversionSequence::Worse;
+        }
+      }
+
+      if ((SCS2.Second == ICK_Floating_Conversion) &&
+          SCS2.getToType(1)->isCXX23FloatingPointType(S.Context) &&
+          S.Context.isCXX23EqualFloatingPointRank(
+              S.Context.getFloatingTypeOrder(SCS2.getToType(1),
+                                             SCS2.getFromType()))) {
+        if (SCS1.getToType(1)->isArithmeticType()) {
+          if (!SCS1.getToType(1)->isCXX23FloatingPointType(S.Context)) {
+            return ImplicitConversionSequence::Worse;
+          }
+
+          if (!S.Context.isCXX23EqualFloatingPointRank(
+                  S.Context.getFloatingTypeOrder(SCS1.getToType(1),
+                                                 SCS2.getFromType()))) {
+            return ImplicitConversionSequence::Worse;
+          }
+
+          if (S.Context.getFloatingTypeOrder(SCS2.getToType(1),
+                                             SCS1.getToType(1)) ==
+              FRCR_Equal_Greater_Subrank) {
+            return ImplicitConversionSequence::Worse;
+          }
+
+          assert(false && "Should not reach here");
+        }
+      }
+    }
+  }
+
   // C++ [over.ics.rank]p4b2:
   //
   //   If class B is derived directly or indirectly from class A,
@@ -8571,7 +8648,7 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty,
 
   // Flag if we encounter an arithmetic type.
   HasArithmeticOrEnumeralTypes =
-    HasArithmeticOrEnumeralTypes || Ty->isArithmeticType();
+      HasArithmeticOrEnumeralTypes || Ty->isArithmeticType();
 
   if (Ty->isObjCIdType() || Ty->isObjCClassType())
     PointerTypes.insert(Ty);
diff --git a/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp b/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp
new file mode 100644
index 00000000000000..736948b5efb7b7
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp
@@ -0,0 +1,499 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-linux-gnu -target-feature +fullbf16 -S -emit-llvm %s -o - | FileCheck %s  --check-prefix=CHECK
+
+// CHECK-LABEL: define {{[^@]+}}@_Z1fDF16b
+// CHECK-SAME: (bfloat noundef [[V:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK:         [[V_ADDR:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    store bfloat [[V]], ptr [[V_ADDR]], align 2
+// CHECK-NEXT:    ret i32 1
+//
+int f(decltype(0.BF16) v) { return 1; }
+// CHECK-LABEL: define {{[^@]+}}@_Z1fDF16_
+// CHECK-SAME: (half noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK:         [[V_ADDR:%.*]] = alloca half, align 2
+// CHECK-NEXT:    store half [[V]], ptr [[V_ADDR]], align 2
+// CHECK-NEXT:    ret i32 1
+//
+int f(_Float16 v) { return 1; }
+// CHECK-LABEL: define {{[^@]+}}@_Z1ff
+// CHECK-SAME: (float noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK:         [[V_ADDR:%.*]] = alloca float, align 4
+// CHECK-NEXT:    store float [[V]], ptr [[V_ADDR]], align 4
+// CHECK-NEXT:    ret i32 1
+//
+int f(float v) { return 1; }
+// CHECK-LABEL: define {{[^@]+}}@_Z1fd
+// CHECK-SAME: (double noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK:         [[V_ADDR:%.*]] = alloca double, align 8
+// CHECK-NEXT:    store double [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    ret i32 1
+//
+int f(double v) { return 1; }
+// CHECK-LABEL: define {{[^@]+}}@_Z1fe
+// CHECK-SAME: (x86_fp80 noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK:         [[V_ADDR:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    store x86_fp80 [[V]], ptr [[V_ADDR]], align 16
+// CHECK-NEXT:    ret i32 1
+//
+int f(long double v) { return 1; }
+// CHECK-LABEL: define {{[^@]+}}@_Z1fi
+// CHECK-SAME: (i32 noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK:         [[V_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[V]], ptr [[V_ADDR]], align 4
+// CHECK-NEXT:    ret i32 1
+//
+int f(int v) { return 1; }
+
+// CHECK-LABEL: define {{[^@]+}}@_Z3f_2e
+// CHECK-SAME: (x86_fp80 noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK:         [[V_ADDR:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    store x86_fp80 [[V]], ptr [[V_ADDR]], align 16
+// CHECK-NEXT:    ret i32 1
+//
+int f_2(long double v) { return 1; }
+
+struct S {
+
+    operator decltype(0.BF16)() const {
+        return 0.0bf16;
+    }
+
+    operator _Float16() const {
+        return 0.0f16;
+    }
+
+    operator float() const {
+        return 0.0f;
+    }
+
+    operator double() const {
+        return 0.0;
+    }
+
+    operator long double() const {
+        return 0.0L;
+    }
+
+    operator int() const {
+        return 0;
+    }
+};
+
+struct S1 {
+    operator _Float16() const {
+        return 0.0f16;
+    }
+    operator float() const {
+        return 0.0f;
+    }
+    operator double() const {
+        return 0.0;
+    }
+    operator long double() const {
+        return 0.0L;
+    }
+
+    operator int() const {
+        return 0;
+    }
+};
+
+// CHECK-LABEL: define {{[^@]+}}@_Z5test1v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[F16_VAL_6:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_VAL_8:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_VAL_9:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_VAL_10:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_VAL_11:%.*]] = alloca half, align 2
+// CHECK-NEXT:    store half 0xH3C00, ptr [[F16_VAL_6]], align 2
+// CHECK-NEXT:    store half 0xH3C00, ptr [[F16_VAL_8]], align 2
+// CHECK-NEXT:    store half 0xH3C00, ptr [[F16_VAL_9]], align 2
+// CHECK-NEXT:    store half 0xH3C00, ptr [[F16_VAL_10]], align 2
+// CHECK-NEXT:    store half 0xH3C00, ptr [[F16_VAL_11]], align 2
+// CHECK-NEXT:    ret void
+//
+void test1() {
+    _Float16 f16_val_6 = 1.0f16;
+    _Float16 f16_val_8 = static_cast<_Float16>(1.0f);
+    _Float16 f16_val_9 = static_cast<_Float16>(1.0);
+    _Float16 f16_val_10 = static_cast<_Float16>(1.0l);
+    _Float16 f16_val_11 = static_cast<_Float16>(1.0f16);
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z5test2v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[BF16_VAL_5:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_VAL_7:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_VAL_8:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_VAL_9:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_VAL_10:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    store bfloat 0xR3F80, ptr [[BF16_VAL_5]], align 2
+// CHECK-NEXT:    store bfloat 0xR3F80, ptr [[BF16_VAL_7]], align 2
+// CHECK-NEXT:    store bfloat 0xR3F80, ptr [[BF16_VAL_8]], align 2
+// CHECK-NEXT:    store bfloat 0xR3F80, ptr [[BF16_VAL_9]], align 2
+// CHECK-NEXT:    store bfloat 0xR3F80, ptr [[BF16_VAL_10]], align 2
+// CHECK-NEXT:    ret void
+//
+void test2() {
+    decltype(0.BF16) bf16_val_5 = 1.0bf16;
+    decltype(0.BF16) bf16_val_7 = static_cast<decltype(0.BF16)>(1.0f);
+    decltype(0.BF16) bf16_val_8 = static_cast<decltype(0.BF16)>(1.0);
+    decltype(0.BF16) bf16_val_9 = static_cast<decltype(0.BF16)>(1.0l);
+    decltype(0.BF16) bf16_val_10 = static_cast<decltype(0.BF16)>(1.0bf16);
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z5test3v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[F_VAL_1:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[F_VAL_2:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[F_VAL_3:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[F_VAL_4:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[F_VAL_5:%.*]] = alloca float, align 4
+// CHECK-NEXT:    store float 1.000000e+00, ptr [[F_VAL_1]], align 4
+// CHECK-NEXT:    store float 1.000000e+00, ptr [[F_VAL_2]], align 4
+// CHECK-NEXT:    store float 1.000000e+00, ptr [[F_VAL_3]], align 4
+// CHECK-NEXT:    store float 1.000000e+00, ptr [[F_VAL_4]], align 4
+// CHECK-NEXT:    store float 1.000000e+00, ptr [[F_VAL_5]], align 4
+// CHECK-NEXT:    ret void
+//
+void test3() {
+    float f_val_1 = 1.0f16;
+    float f_val_2 = 1.0bf16;
+    float f_val_3 = 1.0;
+    float f_val_4 = 1.0l;
+    float f_val_5 = 1.0f;
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z5test4v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[D_VAL_1:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[D_VAL_2:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[D_VAL_3:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[D_VAL_4:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[D_VAL_5:%.*]] = alloca double, align 8
+// CHECK-NEXT:    store double 1.000000e+00, ptr [[D_VAL_1]], align 8
+// CHECK-NEXT:    store double 1.000000e+00, ptr [[D_VAL_2]], align 8
+// CHECK-NEXT:    store double 1.000000e+00, ptr [[D_VAL_3]], align 8
+// CHECK-NEXT:    store double 1.000000e+00, ptr [[D_VAL_4]], align 8
+// CHECK-NEXT:    store double 1.000000e+00, ptr [[D_VAL_5]], align 8
+// CHECK-NEXT:    ret void
+//
+void test4() {
+    double d_val_1 = 1.0f16;
+    double d_val_2 = 1.0bf16;
+    double d_val_3 = 1.0f;
+    double d_val_4 = 1.0l;
+    double d_val_5 = 1.0;
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z5test5v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[LD_VAL_1:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    [[LD_VAL_2:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    [[LD_VAL_3:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    [[LD_VAL_4:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    [[LD_VAL_5:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    store x86_fp80 0xK3FFF8000000000000000, ptr [[LD_VAL_1]], align 16
+// CHECK-NEXT:    store x86_fp80 0xK3FFF8000000000000000, ptr [[LD_VAL_2]], align 16
+// CHECK-NEXT:    store x86_fp80 0xK3FFF8000000000000000, ptr [[LD_VAL_3]], align 16
+// CHECK-NEXT:    store x86_fp80 0xK3FFF8000000000000000, ptr [[LD_VAL_4]], align 16
+// CHECK-NEXT:    store x86_fp80 0xK3FFF8000000000000000, ptr [[LD_VAL_5]], align 16
+// CHECK-NEXT:    ret void
+//
+void test5() {
+    long double ld_val_1 = 1.0f16;
+    long double ld_val_2 = 1.0bf16;
+    long double ld_val_3 = 1.0f;
+    long double ld_val_4 = 1.0;
+    long double ld_val_5 = 1.0l;
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z5test6v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[F16_FLOAT:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[F16_DOUBLE:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[F16_LDOUBLE:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    [[F16_INT:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_UINT:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_LONG:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_ULONG:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_LLONG:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_ULLONG:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[F16_BOOL:%.*]] = alloca half, align 2
+// CHECK-NEXT:    store float 2.000000e+00, ptr [[F16_FLOAT]], align 4
+// CHECK-NEXT:    store double 2.000000e+00, ptr [[F16_DOUBLE]], align 8
+// CHECK-NEXT:    store x86_fp80 0xK40008000000000000000, ptr [[F16_LDOUBLE]], align 16
+// CHECK-NEXT:    store half 0xH4000, ptr [[F16_INT]], align 2
+// CHECK-NEXT:    store half 0xH4000, ptr [[F16_UINT]], align 2
+// CHECK-NEXT:    store half 0xH4000, ptr [[F16_LONG]], align 2
+// CHECK-NEXT:    store half 0xH4000, ptr [[F16_ULONG]], align 2
+// CHECK-NEXT:    store half 0xH4000, ptr [[F16_LLONG]], align 2
+// CHECK-NEXT:    store half 0xH4000, ptr [[F16_ULLONG]], align 2
+// CHECK-NEXT:    store half 0xH4000, ptr [[F16_BOOL]], align 2
+// CHECK-NEXT:    ret void
+//
+void test6() {
+    auto f16_float = 1.0f16 + 1.0f;
+    auto f16_double = 1.0f16 + 1.0;
+    auto f16_ldouble = 1.0f16 + 1.0l;
+    auto f16_int = 1.0f16 + 1;
+    auto f16_uint = 1.0f16 + 1u;
+    auto f16_long = 1.0f16 + 1l;
+    auto f16_ulong = 1.0f16 + 1ul;
+    auto f16_llong = 1.0f16 + 1ll;
+    auto f16_ullong = 1.0f16 + 1ull;
+    auto f16_bool = 1.0f16 + true;
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z5test7v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[BF16_FLOAT:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[BF16_DOUBLE:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[BF16_LDOUBLE:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    [[BF16_INT:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_UINT:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_LONG:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_ULONG:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_LLONG:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_ULLONG:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[BF16_BOOL:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    store float 2.000000e+00, ptr [[BF16_FLOAT]], align 4
+// CHECK-NEXT:    store double 2.000000e+00, ptr [[BF16_DOUBLE]], align 8
+// CHECK-NEXT:    store x86_fp80 0xK40008000000000000000, ptr [[BF16_LDOUBLE]], align 16
+// CHECK-NEXT:    store bfloat 0xR4000, ptr [[BF16_INT]], align 2
+// CHECK-NEXT:    store bfloat 0xR4000, ptr [[BF16_UINT]], align 2
+// CHECK-NEXT:    store bfloat 0xR4000, ptr [[BF16_LONG]], align 2
+// CHECK-NEXT:    store bfloat 0xR4000, ptr [[BF16_ULONG]], align 2
+// CHECK-NEXT:    store bfloat 0xR4000, ptr [[BF16_LLONG]], align 2
+// CHECK-NEXT:    store bfloat 0xR4000, ptr [[BF16_ULLONG]], align 2
+// CHECK-NEXT:    store bfloat 0xR4000, ptr [[BF16_BOOL]], align 2
+// CHECK-NEXT:    ret void
+//
+void test7() {
+    auto bf16_float = 1.0bf16 + 1.0f;
+    //
+    auto bf16_double = 1.0bf16 + 1.0;
+    auto bf16_ldouble = 1.0bf16 + 1.0l;
+    auto bf16_int = 1.0bf16 + 1;
+    auto bf16_uint = 1.0bf16 + 1u;
+    auto bf16_long = 1.0bf16 + 1l;
+    auto bf16_ulong = 1.0bf16 + 1ul;
+    auto bf16_llong = 1.0bf16 + 1ll;
+    auto bf16_ullong = 1.0bf16 + 1ull;
+    auto bf16_bool = 1.0bf16 + true;
+}
+
+//
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test8_1v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[BF16_VAL:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[TEST1:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[TEST_9:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store bfloat 0xR3F80, ptr [[BF16_VAL]], align 2
+// CHECK-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[BF16_VAL]], align 2
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef i32 @_Z1fDF16b(bfloat noundef [[TMP0]])
+// CHECK-NEXT:    store i32 [[CALL]], ptr [[TEST1]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[BF16_VAL]], align 2
+// CHECK-NEXT:    [[CONV:%.*]] = fpext bfloat [[TMP1]] to x86_fp80
+// CHECK-NEXT:    [[CALL1:%.*]] = call noundef i32 @_Z3f_2e(x86_fp80 noundef [[CONV]])
+// CHECK-NEXT:    store i32 [[CALL1]], ptr [[TEST_9]], align 4
+// CHECK-NEXT:    ret void
+//
+void test8_1() {
+    decltype(0.BF16) bf16_val = 1.0bf16;
+    int test1 = f(bf16_val); // calls f(decltype(0.BF16))
+    int test_9 = f_2(bf16_val);
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test8_2v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[FLOAT16_VAL:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[TEST2:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[TEST_10:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store half 0xH3C00, ptr [[FLOAT16_VAL]], align 2
+// CHECK-NEXT:    [[TMP0:%.*]] = load half, ptr [[FLOAT16_VAL]], align 2
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef i32 @_Z1fDF16_(half noundef [[TMP0]])
+// CHECK-NEXT:    store i32 [[CALL]], ptr [[TEST2]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load half, ptr [[FLOAT16_VAL]], align 2
+// CHECK-NEXT:    [[CONV:%.*]] = fpext half [[TMP1]] to x86_fp80
+// CHECK-NEXT:    [[CALL1:%.*]] = call noundef i32 @_Z3f_2e(x86_fp80 noundef [[CONV]])
+// CHECK-NEXT:    store i32 [[CALL1]], ptr [[TEST_10]], align 4
+// CHECK-NEXT:    ret void
+//
+void test8_2() {
+    _Float16 float16_val = 1.0f16;
+    int test2 = f(float16_val); // calls f(_Float16)
+    int test_10 = f_2(float16_val);
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test8_3v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[FLOAT_VAL:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[TEST3:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[TEST_7:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store float 1.000000e+00, ptr [[FLOAT_VAL]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load float, ptr [[FLOAT_VAL]], align 4
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef i32 @_Z1ff(float noundef [[TMP0]])
+// CHECK-NEXT:    store i32 [[CALL]], ptr [[TEST3]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load float, ptr [[FLOAT_VAL]], align 4
+// CHECK-NEXT:    [[CONV:%.*]] = fpext float [[TMP1]] to x86_fp80
+// CHECK-NEXT:    [[CALL1:%.*]] = call noundef i32 @_Z3f_2e(x86_fp80 noundef [[CONV]])
+// CHECK-NEXT:    store i32 [[CALL1]], ptr [[TEST_7]], align 4
+// CHECK-NEXT:    ret void
+//
+void test8_3() {
+    float float_val = 1.0f;
+    int test3 = f(float_val); // calls f(float)
+    int test_7 = f_2(float_val);
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test8_4v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[DOUBLE_VAL:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[TEST4:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[TEST_8:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store double 1.000000e+00, ptr [[DOUBLE_VAL]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load double, ptr [[DOUBLE_VAL]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef i32 @_Z1fd(double noundef [[TMP0]])
+// CHECK-NEXT:    store i32 [[CALL]], ptr [[TEST4]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load double, ptr [[DOUBLE_VAL]], align 8
+// CHECK-NEXT:    [[CONV:%.*]] = fpext double [[TMP1]] to x86_fp80
+// CHECK-NEXT:    [[CALL1:%.*]] = call noundef i32 @_Z3f_2e(x86_fp80 noundef [[CONV]])
+// CHECK-NEXT:    store i32 [[CALL1]], ptr [[TEST_8]], align 4
+// CHECK-NEXT:    ret void
+//
+void test8_4() {
+    double double_val = 1.0;
+    int test4 = f(double_val); // calls f(double)
+    int test_8 = f_2(double_val);
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test8_5v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[LONG_DOUBLE_VAL:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    [[TEST5:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store x86_fp80 0xK3FFF8000000000000000, ptr [[LONG_DOUBLE_VAL]], align 16
+// CHECK-NEXT:    [[TMP0:%.*]] = load x86_fp80, ptr [[LONG_DOUBLE_VAL]], align 16
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef i32 @_Z1fe(x86_fp80 noundef [[TMP0]])
+// CHECK-NEXT:    store i32 [[CALL]], ptr [[TEST5]], align 4
+// CHECK-NEXT:    ret void
+//
+void test8_5() {
+    long double long_double_val = 1.0l;
+    int test5 = f(long_double_val); // calls f(long double)
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test8_6v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[INT_VAL:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[TEST6:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 1, ptr [[INT_VAL]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[INT_VAL]], align 4
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef i32 @_Z1fi(i32 noundef [[TMP0]])
+// CHECK-NEXT:    store i32 [[CALL]], ptr [[TEST6]], align 4
+// CHECK-NEXT:    ret void
+//
+void test8_6() {
+    int int_val = 1;
+    int test6 = f(int_val); // calls f(int)
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test9_1v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[USER_DEFINED_VAL:%.*]] = alloca [[STRUCT_S:%.*]], align 1
+// CHECK-NEXT:    [[BFLOAT16_VAL:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef bfloat @_ZNK1ScvDF16bEv(ptr noundef nonnull align 1 dereferenceable(1) [[USER_DEFINED_VAL]])
+// CHECK-NEXT:    store bfloat [[CALL]], ptr [[BFLOAT16_VAL]], align 2
+// CHECK-NEXT:    ret void
+//
+void test9_1() {
+    S user_defined_val;
+    // User-defined overload cases
+    decltype(0.BF16) bfloat16_val(user_defined_val); // calls operator decltype(0.BF16)()
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test9_2v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[USER_DEFINED_VAL:%.*]] = alloca [[STRUCT_S:%.*]], align 1
+// CHECK-NEXT:    [[F16_VAL:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef half @_ZNK1ScvDF16_Ev(ptr noundef nonnull align 1 dereferenceable(1) [[USER_DEFINED_VAL]])
+// CHECK-NEXT:    store half [[CALL]], ptr [[F16_VAL]], align 2
+// CHECK-NEXT:    ret void
+//
+void test9_2() {
+    S user_defined_val;
+    // User-defined overload cases
+    _Float16 f16_val(user_defined_val); // calls operator _Float16()
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test9_3v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[USER_DEFINED_VAL:%.*]] = alloca [[STRUCT_S:%.*]], align 1
+// CHECK-NEXT:    [[F_VAL:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef float @_ZNK1ScvfEv(ptr noundef nonnull align 1 dereferenceable(1) [[USER_DEFINED_VAL]])
+// CHECK-NEXT:    store float [[CALL]], ptr [[F_VAL]], align 4
+// CHECK-NEXT:    ret void
+//
+void test9_3() {
+    S user_defined_val;
+    // User-defined overload cases
+    float f_val(user_defined_val); // calls operator float()
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test9_4v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[USER_DEFINED_VAL:%.*]] = alloca [[STRUCT_S:%.*]], align 1
+// CHECK-NEXT:    [[D_VAL:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef double @_ZNK1ScvdEv(ptr noundef nonnull align 1 dereferenceable(1) [[USER_DEFINED_VAL]])
+// CHECK-NEXT:    store double [[CALL]], ptr [[D_VAL]], align 8
+// CHECK-NEXT:    ret void
+//
+void test9_4() {
+    S user_defined_val;
+    // User-defined overload cases
+    double d_val(user_defined_val); // calls operator double()
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test9_5v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[USER_DEFINED_VAL:%.*]] = alloca [[STRUCT_S:%.*]], align 1
+// CHECK-NEXT:    [[LD_VAL:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef x86_fp80 @_ZNK1ScveEv(ptr noundef nonnull align 1 dereferenceable(1) [[USER_DEFINED_VAL]])
+// CHECK-NEXT:    store x86_fp80 [[CALL]], ptr [[LD_VAL]], align 16
+// CHECK-NEXT:    ret void
+//
+void test9_5() {
+    S user_defined_val;
+    // User-defined overload cases
+    long double ld_val(user_defined_val); // calls operator long double()
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test9_6v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[USER_DEFINED_VAL:%.*]] = alloca [[STRUCT_S:%.*]], align 1
+// CHECK-NEXT:    [[I_VAL:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef i32 @_ZNK1ScviEv(ptr noundef nonnull align 1 dereferenceable(1) [[USER_DEFINED_VAL]])
+// CHECK-NEXT:    store i32 [[CALL]], ptr [[I_VAL]], align 4
+// CHECK-NEXT:    ret void
+//
+void test9_6() {
+    S user_defined_val;
+    // User-defined overload cases
+    int i_val(user_defined_val); // calls operator int()
+}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z7test9_7v
+// CHECK-SAME: () #[[ATTR0]] {
+// CHECK:         [[USER_DEFINED_VAL_2:%.*]] = alloca [[STRUCT_S1:%.*]], align 1
+// CHECK-NEXT:    [[BFLOAT16_VAL_2:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef i32 @_ZNK2S1cviEv(ptr noundef nonnull align 1 dereferenceable(1) [[USER_DEFINED_VAL_2]])
+// CHECK-NEXT:    [[CONV:%.*]] = sitofp i32 [[CALL]] to bfloat
+// CHECK-NEXT:    store bfloat [[CONV]], ptr [[BFLOAT16_VAL_2]], align 2
+// CHECK-NEXT:    ret void
+//
+void test9_7() {
+    S1 user_defined_val_2;
+    // User-defined overload cases
+    decltype(0.BF16) bfloat16_val_2(user_defined_val_2); // calls operator int()
+}
diff --git a/clang/test/CodeGenCXX/cxx23-vector-bfloat16.cpp b/clang/test/CodeGenCXX/cxx23-vector-bfloat16.cpp
new file mode 100644
index 00000000000000..8bccddbd3cf083
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx23-vector-bfloat16.cpp
@@ -0,0 +1,67 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-linux-gnu -target-feature +fullbf16 -S -emit-llvm %s -o - | FileCheck %s
+
+typedef decltype(0.0BF16) v8bfloat16 __attribute__((__vector_size__(16)));
+
+// CHECK-LABEL: define dso_local void @_Z11test_vectorDv8_DF16bS_
+// CHECK-SAME: (<8 x bfloat> noundef [[A:%.*]], <8 x bfloat> noundef [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK:         [[A_ADDR:%.*]] = alloca <8 x bfloat>, align 16
+// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca <8 x bfloat>, align 16
+// CHECK-NEXT:    [[C:%.*]] = alloca <8 x bfloat>, align 16
+// CHECK-NEXT:    store <8 x bfloat> [[A]], ptr [[A_ADDR]], align 16
+// CHECK-NEXT:    store <8 x bfloat> [[B]], ptr [[B_ADDR]], align 16
+// CHECK-NEXT:    [[TMP0:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16
+// CHECK-NEXT:    [[TMP1:%.*]] = load <8 x bfloat>, ptr [[B_ADDR]], align 16
+// CHECK-NEXT:    [[ADD:%.*]] = fadd <8 x bfloat> [[TMP0]], [[TMP1]]
+// CHECK-NEXT:    store <8 x bfloat> [[ADD]], ptr [[C]], align 16
+// CHECK-NEXT:    [[TMP2:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16
+// CHECK-NEXT:    [[TMP3:%.*]] = load <8 x bfloat>, ptr [[B_ADDR]], align 16
+// CHECK-NEXT:    [[SUB:%.*]] = fsub <8 x bfloat> [[TMP2]], [[TMP3]]
+// CHECK-NEXT:    store <8 x bfloat> [[SUB]], ptr [[C]], align 16
+// CHECK-NEXT:    [[TMP4:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16
+// CHECK-NEXT:    [[TMP5:%.*]] = load <8 x bfloat>, ptr [[B_ADDR]], align 16
+// CHECK-NEXT:    [[MUL:%.*]] = fmul <8 x bfloat> [[TMP4]], [[TMP5]]
+// CHECK-NEXT:    store <8 x bfloat> [[MUL]], ptr [[C]], align 16
+// CHECK-NEXT:    [[TMP6:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16
+// CHECK-NEXT:    [[TMP7:%.*]] = load <8 x bfloat>, ptr [[B_ADDR]], align 16
+// CHECK-NEXT:    [[DIV:%.*]] = fdiv <8 x bfloat> [[TMP6]], [[TMP7]]
+// CHECK-NEXT:    store <8 x bfloat> [[DIV]], ptr [[C]], align 16
+// CHECK-NEXT:    ret void
+//
+void test_vector(v8bfloat16 a, v8bfloat16 b) {
+    v8bfloat16 c;
+    c = a + b;
+    c = a - b;
+    c = a * b;
+    c = a / b;
+}
+
+// CHECK-LABEL: define dso_local void @_Z13test_vector_2v
+// CHECK-SAME: () #[[ATTR1:[0-9]+]] {
+// CHECK:         [[A:%.*]] = alloca <8 x bfloat>, align 16
+// CHECK-NEXT:    [[DOTCOMPOUNDLITERAL:%.*]] = alloca <8 x bfloat>, align 16
+// CHECK-NEXT:    [[B:%.*]] = alloca <8 x bfloat>, align 16
+// CHECK-NEXT:    [[DOTCOMPOUNDLITERAL1:%.*]] = alloca <8 x bfloat>, align 16
+// CHECK-NEXT:    [[C:%.*]] = alloca <8 x bfloat>, align 16
+// CHECK-NEXT:    [[D:%.*]] = alloca <8 x bfloat>, align 16
+// CHECK-NEXT:    store <8 x bfloat> zeroinitializer, ptr [[DOTCOMPOUNDLITERAL]], align 16
+// CHECK-NEXT:    [[TMP0:%.*]] = load <8 x bfloat>, ptr [[DOTCOMPOUNDLITERAL]], align 16
+// CHECK-NEXT:    store <8 x bfloat> [[TMP0]], ptr [[A]], align 16
+// CHECK-NEXT:    store <8 x bfloat> <bfloat 0xR3F80, bfloat 0xR0000, bfloat 0xR0000, bfloat 0xR0000, bfloat 0xR0000, bfloat 0xR0000, bfloat 0xR0000, bfloat 0xR0000>, ptr [[DOTCOMPOUNDLITERAL1]], align 16
+// CHECK-NEXT:    [[TMP1:%.*]] = load <8 x bfloat>, ptr [[DOTCOMPOUNDLITERAL1]], align 16
+// CHECK-NEXT:    store <8 x bfloat> [[TMP1]], ptr [[B]], align 16
+// CHECK-NEXT:    [[TMP2:%.*]] = load <8 x bfloat>, ptr [[A]], align 16
+// CHECK-NEXT:    [[ELT_ABS:%.*]] = call <8 x bfloat> @llvm.fabs.v8bf16(<8 x bfloat> [[TMP2]])
+// CHECK-NEXT:    store <8 x bfloat> [[ELT_ABS]], ptr [[C]], align 16
+// CHECK-NEXT:    [[TMP3:%.*]] = load <8 x bfloat>, ptr [[C]], align 16
+// CHECK-NEXT:    [[ELT_ABS2:%.*]] = call <8 x bfloat> @llvm.fabs.v8bf16(<8 x bfloat> [[TMP3]])
+// CHECK-NEXT:    store <8 x bfloat> [[ELT_ABS2]], ptr [[D]], align 16
+// CHECK-NEXT:    ret void
+//
+void test_vector_2() {
+    v8bfloat16 a = (v8bfloat16){0.0bf16};
+    v8bfloat16 b = (v8bfloat16){1.0bf16};
+    v8bfloat16 c = __builtin_elementwise_abs(a);
+    v8bfloat16 d = __builtin_elementwise_abs(c);
+}
+
diff --git a/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp b/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp
new file mode 100644
index 00000000000000..23ac2d21d80777
--- /dev/null
+++ b/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp
@@ -0,0 +1,505 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++23 -target-feature +fullbf16 -verify -ast-dump %s | FileCheck %s
+#include <stdarg.h>
+_Float16 f16_val_1 = 1.0bf16; // expected-error {{cannot initialize a variable of type '_Float16' with an rvalue of type '__bf16'}}
+_Float16 f16_val_2 = 1.0f; // expected-error {{cannot initialize a variable of type '_Float16' with an rvalue of type 'float'}}
+_Float16 f16_val_3 = 1.0; // expected-error {{cannot initialize a variable of type '_Float16' with an rvalue of type 'double'}}
+_Float16 f16_val_4 = 1.0l; // expected-error {{cannot initialize a variable of type '_Float16' with an rvalue of type 'long double'}}
+_Float16 f16_val_6 = 1.0f16;
+//CHECK:      VarDecl {{.*}} f16_val_6 '_Float16' cinit
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+_Float16 f16_val_7 = static_cast<_Float16>(1.0bf16); // expected-error {{static_cast from '__bf16' to '_Float16' is not allowed}}
+_Float16 f16_val_8 = static_cast<_Float16>(1.0f);
+//CHECK:      VarDecl {{.*}} f16_val_8 '_Float16' cinit
+//CHECK-NEXT: CXXStaticCastExpr {{.*}} '_Float16' static_cast<_Float16> <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.000000e+00
+_Float16 f16_val_9 = static_cast<_Float16>(1.0);
+//CHECK:      VarDecl {{.*}} f16_val_9 '_Float16' cinit
+//CHECK-NEXT: CXXStaticCastExpr {{.*}} '_Float16' static_cast<_Float16> <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
+_Float16 f16_val_10 = static_cast<_Float16>(1.0l);
+//CHECK:      VarDecl {{.*}} f16_val_10 '_Float16' cinit
+//CHECK-NEXT: CXXStaticCastExpr {{.*}} '_Float16' static_cast<_Float16> <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'long double' 1.000000e+00
+_Float16 f16_val_11 = static_cast<_Float16>(1.0f16);
+//CHECK:      VarDecl {{.*}} f16_val_11 '_Float16' cinit
+//CHECK-NEXT: CXXStaticCastExpr {{.*}} '_Float16' static_cast<_Float16> <NoOp>
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+
+decltype(0.0BF16) bf16_val_1 = 1.0f16; // expected-error {{cannot initialize a variable of type 'decltype(0.BF16)' (aka '__bf16') with an rvalue of type '_Float16'}}
+decltype(0.0BF16) bf16_val_2 = 1.0f; // expected-error {{cannot initialize a variable of type 'decltype(0.BF16)' (aka '__bf16') with an rvalue of type 'float'}}
+decltype(0.0BF16) bf16_val_3 = 1.0; // expected-error {{cannot initialize a variable of type 'decltype(0.BF16)' (aka '__bf16') with an rvalue of type 'double'}}
+decltype(0.0BF16) bf16_val_4 = 1.0l; // expected-error {{cannot initialize a variable of type 'decltype(0.BF16)' (aka '__bf16') with an rvalue of type 'long double'}}
+decltype(0.0BF16) bf16_val_5 = 1.0bf16;
+//CHECK:      VarDecl {{.*}} bf16_val_5 'decltype(0.BF16)':'__bf16' cinit
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+
+decltype(0.0BF16) bf16_val_6 = static_cast<decltype(0.0BF16)>(1.0f16); // expected-error {{static_cast from '_Float16' to 'decltype(0.BF16)' (aka '__bf16') is not allowed}}
+decltype(0.0BF16) bf16_val_7 = static_cast<decltype(0.0BF16)>(1.0f);
+//CHECK:      VarDecl {{.*}} bf16_val_7 'decltype(0.BF16)':'__bf16' cinit
+//CHECK-NEXT: CXXStaticCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' static_cast<decltype(0.BF16)> <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.000000e+00
+decltype(0.0BF16) bf16_val_8 = static_cast<decltype(0.0BF16)>(1.0);
+//CHECK:      VarDecl {{.*}} bf16_val_8 'decltype(0.BF16)':'__bf16' cinit
+//CHECK-NEXT: CXXStaticCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' static_cast<decltype(0.BF16)> <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
+decltype(0.0BF16) bf16_val_9 = static_cast<decltype(0.0BF16)>(1.0l);
+//CHECK:      VarDecl {{.*}} bf16_val_9 'decltype(0.BF16)':'__bf16' cinit
+//CHECK-NEXT: CXXStaticCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' static_cast<decltype(0.BF16)> <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'long double' 1.000000e+00
+decltype(0.0BF16) bf16_val_10 = static_cast<decltype(0.0BF16)>(1.0bf16);
+//CHECK:      VarDecl {{.*}} bf16_val_10 'decltype(0.BF16)':'__bf16' cinit
+//CHECK-NEXT: CXXStaticCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' static_cast<decltype(0.BF16)> <NoOp>
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+
+float f_val_1 = 1.0f16;
+//CHECK:      VarDecl {{.*}} f_val_1 'float' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+float f_val_2 = 1.0bf16;
+//CHECK:      VarDecl {{.*}} f_val_2 'float' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+float f_val_3 = 1.0;
+//CHECK:      VarDecl {{.*}} f_val_3 'float' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
+float f_val_4 = 1.0l;
+//CHECK:      VarDecl {{.*}} f_val_4 'float' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'long double' 1.000000e+00
+float f_val_5 = 1.0f;
+//CHECK:      VarDecl {{.*}} f_val_5 'float' cinit
+//CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.000000e+00
+
+double d_val_1 = 1.0f16;
+//CHECK:      VarDecl {{.*}} d_val_1 'double' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+double d_val_2 = 1.0bf16;
+//CHECK:      VarDecl {{.*}} d_val_2 'double' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+double d_val_3 = 1.0f;
+//CHECK:      VarDecl {{.*}} d_val_3 'double' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.000000e+00
+double d_val_4 = 1.0l;
+//CHECK:      VarDecl {{.*}} d_val_4 'double' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'long double' 1.000000e+00
+double d_val_5 = 1.0;
+//CHECK:      VarDecl {{.*}} d_val_5 'double' cinit
+//CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
+
+long double ld_val_1 = 1.0f16;
+//CHECK:      VarDecl {{.*}} ld_val_1 'long double' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+long double ld_val_2 = 1.0bf16;
+//CHECK:      VarDecl {{.*}} ld_val_2 'long double' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+long double ld_val_3 = 1.0f;
+//CHECK:      VarDecl {{.*}} ld_val_3 'long double' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.000000e+00
+long double ld_val_4 = 1.0;
+//CHECK:      VarDecl {{.*}} ld_val_4 'long double' cinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
+long double ld_val_5 = 1.0l;
+//CHECK:      VarDecl {{.*}} ld_val_5 'long double' cinit
+//CHECK-NEXT: FloatingLiteral {{.*}} 'long double' 1.000000e+00
+
+auto f16_bf16 = 1.0f16 + 1.0bf16; // expected-error {{invalid operands to binary expression ('_Float16' and '__bf16')}}
+auto f16_bf16_cast = 1.0f16 + static_cast<_Float16>(1.0bf16); // expected-error {{static_cast from '__bf16' to '_Float16' is not allowed}}
+auto f16_float = 1.0f16 + 1.0f;
+//CHECK:      VarDecl {{.*}} f16_float 'float' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} 'float' '+'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.000000e+00
+auto f16_double = 1.0f16 + 1.0;
+//CHECK:      VarDecl {{.*}} f16_double 'double' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} 'double' '+'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
+auto f16_ldouble = 1.0f16 + 1.0l;
+//CHECK:      VarDecl {{.*}} f16_ldouble 'long double' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} 'long double' '+'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: FloatingLiteral {{.*}} 'long double' 1.000000e+00
+auto f16_int = 1.0f16 + 1;
+//CHECK:      VarDecl {{.*}} f16_int '_Float16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
+auto f16_uint = 1.0f16 + 1u;
+//CHECK:      VarDecl {{.*}} f16_uint '_Float16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned int' 1
+auto f16_long = 1.0f16 + 1l;
+//CHECK:      VarDecl {{.*}} f16_long '_Float16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'long' 1
+auto f16_ulong = 1.0f16 + 1ul;
+//CHECK:      VarDecl {{.*}} f16_ulong '_Float16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned long' 1
+auto f16_llong = 1.0f16 + 1ll;
+//CHECK:      VarDecl {{.*}} f16_llong '_Float16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'long long' 1
+auto f16_ullong = 1.0f16 + 1ull;
+//CHECK:      VarDecl {{.*}} f16_ullong '_Float16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned long long' 1
+auto f16_bool = 1.0f16 + true;
+//CHECK:      VarDecl {{.*}} f16_bool '_Float16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <IntegralToFloating>
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <IntegralCast>
+//CHECK-NEXT: CXXBoolLiteralExpr {{.*}} 'bool' true
+
+auto bf16_fp16 = 1.0bf16 + 1.0f16; // expected-error {{invalid operands to binary expression ('__bf16' and '_Float16')}}
+auto bf16_fp16_cast = 1.0bf16 + static_cast<decltype(0.0BF16)>(1.0f16); // expected-error {{static_cast from '_Float16' to 'decltype(0.BF16)' (aka '__bf16') is not allowed}}
+auto bf16_float = 1.0bf16 + 1.0f;
+//CHECK:      VarDecl {{.*}} bf16_float 'float' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} 'float' '+'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.000000e+00
+auto bf16_double = 1.0bf16 + 1.0;
+//CHECK:      VarDecl {{.*}} bf16_double 'double' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} 'double' '+'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
+auto bf16_ldouble = 1.0bf16 + 1.0l;
+//CHECK:      VarDecl {{.*}} bf16_ldouble 'long double' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} 'long double' '+'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: FloatingLiteral {{.*}} 'long double' 1.000000e+00
+auto bf16_int = 1.0bf16 + 1;
+//CHECK:      VarDecl {{.*}} bf16_int '__bf16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '__bf16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '__bf16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
+auto bf16_uint = 1.0bf16 + 1u;
+//CHECK:      VarDecl {{.*}} bf16_uint '__bf16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '__bf16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '__bf16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned int' 1
+auto bf16_long = 1.0bf16 + 1l;
+//CHECK:      VarDecl {{.*}} bf16_long '__bf16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '__bf16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '__bf16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'long' 1
+auto bf16_ulong = 1.0bf16 + 1ul;
+//CHECK:      VarDecl {{.*}} bf16_ulong '__bf16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '__bf16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '__bf16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned long' 1
+auto bf16_llong = 1.0bf16 + 1ll;
+//CHECK:      VarDecl {{.*}} bf16_llong '__bf16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '__bf16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '__bf16' <IntegralToFloating>
+//CHECK-NEXT:  IntegerLiteral {{.*}} 'long long' 1
+auto bf16_ullong = 1.0bf16 + 1ull;
+//CHECK:      VarDecl {{.*}} bf16_ullong '__bf16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '__bf16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '__bf16' <IntegralToFloating>
+//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned long long' 1
+auto bf16_bool = 1.0bf16 + true;
+//CHECK:      VarDecl {{.*}} bf16_bool '__bf16' cinit
+//CHECK-NEXT: BinaryOperator {{.*}} '__bf16' '+'
+//CHECK-NEXT: FloatingLiteral {{.*}} '__bf16' 1.000000e+00
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '__bf16' <IntegralToFloating>
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <IntegralCast>
+//CHECK-NEXT: CXXBoolLiteralExpr {{.*}} 'bool' true
+
+int f(decltype(0.0BF16)) {}
+int f(_Float16) {}
+int f(float) {}
+int f(double) {}
+int f(long double) {}
+int f(int) {}
+
+decltype(0.0BF16) bf16_val = 1.0bf16;
+_Float16 float16_val = 1.0f16;
+float float_val = 1.0f;
+double double_val = 1.0;
+long double long_double_val = 1.0l;
+int int_val = 1;
+
+int test1 = f(bf16_val); // calls f(decltype(0.BF16))
+//CHECK:      VarDecl {{.*}} test1 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(decltype(0.BF16))' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (decltype(0.BF16))' lvalue Function {{.*}} 'f' 'int (decltype(0.BF16))'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'decltype(0.BF16)':'__bf16' lvalue Var {{.*}} 'bf16_val' 'decltype(0.BF16)':'__bf16'
+int test2 = f(float16_val); // calls f(_Float16)
+//CHECK:      VarDecl {{.*}} test2 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(_Float16)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (_Float16)' lvalue Function {{.*}} 'f' 'int (_Float16)'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} '_Float16' lvalue Var {{.*}} 'float16_val' '_Float16'
+int test3 = f(float_val); // calls f(float)
+//CHECK:      VarDecl {{.*}} test3 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(float)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (float)' lvalue Function {{.*}} 'f' 'int (float)'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue Var {{.*}} 'float_val' 'float'
+int test4 = f(double_val); // calls f(double)
+//CHECK:      VarDecl {{.*}} test4 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(double)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (double)' lvalue Function {{.*}} 'f' 'int (double)'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'double' lvalue Var {{.*}} 'double_val' 'double'
+int test5 = f(long_double_val); // calls f(long double)
+//CHECK:      VarDecl {{.*}} test5 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(long double)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f' 'int (long double)'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'long double' lvalue Var {{.*}} 'long_double_val' 'long double'
+int test6 = f(int_val); // calls f(int)
+//CHECK:      VarDecl {{.*}} test6 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(int)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (int)' lvalue Function {{.*}} 'f' 'int (int)'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'int_val' 'int'
+
+int f_1(float) {} // expected-note {{candidate function}} expected-note {{candidate function}}
+int f_1(double) {} // expected-note {{candidate function}} expected-note {{candidate function}}
+
+// Ambiguous cases
+int test_7 = f_1(bf16_val); // expected-error {{call to 'f_1' is ambiguous}}
+int test_8 = f_1(float16_val); // expected-error {{call to 'f_1' is ambiguous}}
+
+int f_2(long double) {}
+int test_9 = f_2(float_val);
+//CHECK:      VarDecl {{.*}} test_9 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(long double)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f_2' 'int (long double)'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue Var {{.*}} 'float_val' 'float'
+int test_10 = f_2(double_val);
+//CHECK:      VarDecl {{.*}} test_10 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(long double)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f_2' 'int (long double)'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'double' lvalue Var {{.*}} 'double_val' 'double'
+int test_11 = f_2(bf16_val);
+//CHECK:      VarDecl {{.*}} test_11 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(long double)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f_2' 'int (long double)'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'decltype(0.BF16)':'__bf16' lvalue Var {{.*}} 'bf16_val' 'decltype(0.BF16)':'__bf16'
+int test_12 = f_2(float16_val);
+//CHECK:      VarDecl {{.*}} test_12 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(long double)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f_2' 'int (long double)'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast>
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} '_Float16' lvalue Var {{.*}} 'float16_val' '_Float16'
+
+int f_3(_Float16) {} // expected-note {{candidate function not viable: no known conversion from 'float' to '_Float16' for 1st argument}} expected-note {{no known conversion from 'decltype(0.BF16)' (aka '__bf16') to '_Float16' for 1st argument}}
+int test_13 = f_3(float_val); // expected-error {{no matching function for call to 'f_3'}}
+int test_14 = f_3(bf16_val); // expected-error {{no matching function for call to 'f_3'}}
+int test_15 = f_3(static_cast<_Float16>(bf16_val)); // expected-error {{static_cast from 'decltype(0.BF16)' (aka '__bf16') to '_Float16' is not allowed}}
+
+int f_4(decltype(0.0BF16)) {} // expected-note {{candidate function not viable: no known conversion from 'float' to 'decltype(0.BF16)' (aka '__bf16') for 1st argument}} expected-note {{candidate function not viable: no known conversion from '_Float16' to 'decltype(0.BF16)' (aka '__bf16') for 1st argument}}
+int test_16 = f_4(float_val); // expected-error {{no matching function for call to 'f_4'}}
+int test_17 = f_4(float16_val); // expected-error {{no matching function for call to 'f_4'}}
+int test_18 = f_4(static_cast<decltype(0.0BF16)>(float16_val)); // expected-error {{static_cast from '_Float16' to 'decltype(0.BF16)' (aka '__bf16') is not allowed}}
+
+struct S {
+  operator decltype(0.0BF16)() const {
+      return 0.0bf16;
+  }
+  operator _Float16() const {
+      return 0.0f16;
+  }
+  operator float() const {
+      return 0.0f;
+  }
+  operator double() const {
+      return 0.0;
+  }
+  operator long double() const {
+      return 0.0L;
+  }
+  operator int() const {
+      return 0;
+  }
+};
+
+
+S user_defined_val;
+// User-defined overload cases
+decltype(0.0BF16) bfloat16_val(user_defined_val); // calls operator decltype(0.BF16)()
+//CHECK:      VarDecl {{.*}} bfloat16_val 'decltype(0.BF16)':'__bf16' callinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' <UserDefinedConversion>
+//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'decltype(0.BF16)':'__bf16'
+//CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator __bf16 {{.*}}
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue <NoOp>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S'
+_Float16 f16_val(user_defined_val); // calls operator _Float16()
+//CHECK:      VarDecl {{.*}} f16_val '_Float16' callinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <UserDefinedConversion>
+//CHECK-NEXT: CXXMemberCallExpr {{.*}} '_Float16
+//CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator _Float16 {{.*}}
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue <NoOp>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S'
+float f_val(user_defined_val); // calls operator float()
+//CHECK:      VarDecl {{.*}} f_val 'float' callinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <UserDefinedConversion>
+//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'float'
+//CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator float {{.*}}
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue <NoOp>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S'
+double d_val(user_defined_val); // calls operator double()
+//CHECK:      VarDecl {{.*}} d_val 'double' callinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <UserDefinedConversion>
+//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'double'
+//CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator double {{.*}}
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue <NoOp>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S'
+long double ld_val(user_defined_val); // calls operator long double()
+//CHECK:      VarDecl {{.*}} ld_val 'long double' callinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <UserDefinedConversion>
+//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'long double'
+//CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator long double {{.*}}
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue <NoOp>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S'
+int i_val(user_defined_val); // calls operator int()
+//CHECK:      VarDecl {{.*}} i_val 'int' callinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <UserDefinedConversion>
+//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'int'
+//CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator int {{.*}}
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue <NoOp>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S'
+struct S1 {
+  operator _Float16() const {
+      return 0.0f16;
+  }
+  operator float() const {
+      return 0.0f;
+  }
+  operator double() const {
+      return 0.0;
+  }
+  operator long double() const {
+      return 0.0L;
+  }
+  operator int() const {
+      return 0;
+  }
+};
+
+S1 user_defined_val_2;
+// User-defined overload cases
+decltype(0.0BF16) bfloat16_val_2(user_defined_val_2); // calls operator int()
+//CHECK:      VarDecl {{.*}} bfloat16_val_2 'decltype(0.BF16)':'__bf16' callinit
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' <IntegralToFloating>
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <UserDefinedConversion>
+//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'int'
+//CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator int {{.*}}
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S1' lvalue <NoOp>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'S1' lvalue Var {{.*}} 'user_defined_val_2' 'S1'
+struct S2 {
+  operator decltype(0.0BF16)() const { // expected-note {{candidate function}}
+      return 0.0bf16;
+  }
+
+  operator _Float16() const { // expected-note {{candidate function}}
+      return 0.0f16;
+  }
+  operator double() const { // expected-note {{candidate function}}
+      return 0.0;
+  }
+  operator long double() const { // expected-note {{candidate function}}
+      return 0.0L;
+  }
+  operator int() const { // expected-note {{candidate function}}
+      return 0;
+  }
+};
+
+S2 user_defined_val_3;
+// User-defined overload cases
+float float_val_2(user_defined_val_3); // expected-error {{conversion from 'S2' to 'float' is ambiguous}}
+
+// Test case for varadic function
+int f_5(int a, ...) {
+    va_list ap;
+    va_start(ap, a);
+    auto bf16_val = va_arg(ap, decltype(0.BF16));
+    auto float16_val = va_arg(ap, _Float16);
+    auto promoted_float_val = va_arg(ap, double);
+    auto double_val = va_arg(ap, double);
+    auto long_double_val = va_arg(ap, long double);
+    auto int_val = va_arg(ap, int);
+    va_end(ap);
+}
+// CHECK: VarDecl {{.*}} used ap 'va_list'
+// CHECK: VarDecl {{.*}} bf16_val 'decltype(0.BF16)'
+// CHECK: VarDecl {{.*}} float16_val '_Float16'
+// CHECK: VarDecl {{.*}} promoted_float_val 'double'
+// CHECK: VarDecl {{.*}} double_val 'double'
+// CHECK: VarDecl {{.*}} long_double_val 'long double'
+// CHECK: VarDecl {{.*}} int_val 'int'
+
+int test_19 = f_5(0, bf16_val, float16_val, float_val, double_val, long_double_val, int_val);
+//CHECK:      VarDecl {{.*}} test_19 'int' cinit
+//CHECK-NEXT: CallExpr {{.*}} 'int'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(int, ...)' <FunctionToPointerDecay>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int (int, ...)' lvalue Function {{.*}} 'f_5' 'int (int, ...)'
+//CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'decltype(0.BF16)':'__bf16' lvalue Var {{.*}} 'bf16_val' 'decltype(0.BF16)':'__bf16'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} '_Float16' lvalue Var {{.*}} 'float16_val' '_Float16'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast>
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue Var {{.*}} 'float_val' 'float'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'double' lvalue Var {{.*}} 'double_val' 'double'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'long double' lvalue Var {{.*}} 'long_double_val' 'long double'
+//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+//CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'int_val' 'int'



More information about the cfe-commits mailing list