[clang] [llvm] [Clang][C++23] Core language changes from P1467R9 extended floating-point types and standard names. (PR #78503)
M. Zeeshan Siddiqui via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 3 15:51:47 PST 2025
https://github.com/codemzs updated https://github.com/llvm/llvm-project/pull/78503
>From 5dd9690681f339b591cbd447fbe7f17f99a33ce2 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 1/2] [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 | 147 ++++-
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 | 80 ++-
.../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, 1507 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 5e75fc447636e0..af045aa8777373 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -248,6 +248,11 @@ C++ Language Changes
- The builtin type alias ``__builtin_common_type`` has been added to improve the
performance of ``std::common_type``.
+- 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 1e89a6805ce9c6..440848bb44e0cd 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -55,6 +55,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;
@@ -1182,8 +1192,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, UnresolvedTemplateTy,
UnknownAnyTy;
@@ -2563,6 +2573,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.
///
@@ -2970,6 +2983,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;
@@ -2990,14 +3006,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 444be4311a7431..af4fd0197554ce 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 09c98f642852fc..238847fbdd7de5 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2513,6 +2513,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 isFloat32Type() const;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 330ae045616aba..ff8118599e1246 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9278,6 +9278,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 705021fcfa5b11..149c6b665946f6 100644
--- a/clang/include/clang/Lex/LiteralSupport.h
+++ b/clang/include/clang/Lex/LiteralSupport.h
@@ -82,6 +82,7 @@ class NumericLiteralParser {
bool isAccum : 1; // 1.0hk/k/lk/uhk/uk/ulk
bool isBitInt : 1; // 1wb, 1uwb (C23) or 1__wb, 1__uwb (Clang extension in C++
// mode)
+ 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 a9ecb4ee9c76b2..5c4ae646e6a1a9 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -132,6 +132,84 @@ template <> struct llvm::DenseMapInfo<llvm::FoldingSetNodeID> {
return LHS == RHS;
}
};
+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.
@@ -1917,6 +1995,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;
}
@@ -7670,27 +7752,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.
@@ -7853,6 +7977,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 c5d19f70fc6ea0..216d3901b0d756 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1458,6 +1458,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 caa0ac858a1bea..8a9a93ede77717 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2289,6 +2289,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 29723b573e771a..0b45d0c05eb8ed 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -470,8 +470,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 225a6c2d15baaa..cce05d55b959b0 100644
--- a/clang/lib/Lex/LiteralSupport.cpp
+++ b/clang/lib/Lex/LiteralSupport.cpp
@@ -927,6 +927,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
@@ -1032,6 +1033,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.
@@ -1190,6 +1206,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 d6517511d7db4d..59db88f0f20d10 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -767,6 +767,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 == CheckedConversionKind::Implicit)) {
+ 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 f98857f852b5af..01b13ab9d1c78a 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -1399,6 +1399,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 ce846ae88c38b4..1c7c30c558b60e 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -7444,9 +7444,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);
}
static analyze_format_string::ArgType::MatchKind
@@ -10871,9 +10873,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);
@@ -11207,7 +11212,7 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
int Order = 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;
@@ -11226,12 +11231,14 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
DiagnoseImpCast(*this, 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 (SourceMgr.isInSystemMacro(CC))
return;
DiagnoseImpCast(*this, 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 562c98c6babe04..66a9eda110c790 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -804,7 +804,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,
@@ -813,7 +813,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(
@@ -1171,14 +1172,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
@@ -1231,16 +1234,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;
@@ -3856,6 +3877,8 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
Ty = Context.Float128Ty;
else if (getLangOpts().HLSL)
Ty = Context.FloatTy;
+ else if (Literal.isBFloat16)
+ Ty = Context.BFloat16Ty;
else
Ty = Context.DoubleTy;
@@ -9914,7 +9937,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;
}
@@ -10108,8 +10132,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 7589701fb81de9..74dc56b100e67c 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -431,7 +431,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);
@@ -2373,6 +2373,15 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
SCS.Second = ICK_Complex_Real;
FromType = ToType.getUnqualifiedType();
} else if (IsFloatingPointConversion(S, FromType, ToType)) {
+ // 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;
// Floating point conversions (C++ 4.8).
SCS.Second = ICK_Floating_Conversion;
FromType = ToType.getUnqualifiedType();
@@ -4446,6 +4455,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,
@@ -8710,7 +8786,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..171e24a3706543
--- /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 -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..90340b29808de2
--- /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 -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'
>From e11a197c0c1ad74dd3d9b25a5ef32d943f73097f Mon Sep 17 00:00:00 2001
From: "M. Zeeshan Siddiqui" <mzs at microsoft.com>
Date: Fri, 3 Jan 2025 12:23:18 -0800
Subject: [PATCH 2/2] PR feedback.
---
clang/include/clang/AST/ASTContext.h | 4 +-
clang/lib/AST/ASTContext.cpp | 108 ++++++++----------
clang/lib/AST/Type.cpp | 6 +-
clang/lib/Frontend/InitPreprocessor.cpp | 8 +-
clang/lib/Lex/LiteralSupport.cpp | 20 ++--
clang/lib/Sema/SemaChecking.cpp | 3 +-
clang/lib/Sema/SemaOverload.cpp | 2 +-
.../cxx23-fp-ext-std-names-p1467r9.cpp | 66 ++++++++++-
.../Sema/cxx23-fp-ext-std-names-p1467r9.cpp | 2 +-
llvm/include/llvm/ADT/APFloat.h | 3 +
llvm/lib/Support/APFloat.cpp | 10 ++
11 files changed, 153 insertions(+), 79 deletions(-)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 440848bb44e0cd..c3e334bd188e5f 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3014,13 +3014,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// 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;
+ 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 FRCR_Equal).
FloatConvRankCompareResult getFloatingTypeSemanticOrder(QualType LHS,
- QualType RHS) const;
+ QualType RHS) const;
/// C++23 6.8.2p12 [basic.fundamental]
/// Checks if extended floating point rules apply to a pair of types.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 5c4ae646e6a1a9..de76e17b730120 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -65,6 +65,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/XRayLists.h"
#include "llvm/ADT/APFixedPoint.h"
+#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/ArrayRef.h"
@@ -152,64 +153,50 @@ constexpr unsigned CXX23FloatRankToIndex(clang::BuiltinType::Kind Kind) {
}
// 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}}}};
+FloatConvRankCompareResult
+CXX23CompareFpConversionRanks(BuiltinType::Kind LHSKind,
+ BuiltinType::Kind RHSKind, QualType LHS,
+ QualType RHS, const ASTContext &Ctx) {
+
+ // Same types.
+ if (LHSKind == RHSKind)
+ return FloatConvRankCompareResult::FRCR_Equal;
+
+ // Special case comparision between float, double and long double.
+ if (LHSKind == BuiltinType::Float && RHSKind == BuiltinType::Double)
+ return FloatConvRankCompareResult::FRCR_Lesser;
+ if (LHSKind == BuiltinType::Double && RHSKind == BuiltinType::Float)
+ return FloatConvRankCompareResult::FRCR_Greater;
+ if (LHSKind == BuiltinType::Float && RHSKind == BuiltinType::LongDouble)
+ return FloatConvRankCompareResult::FRCR_Lesser;
+ if (LHSKind == BuiltinType::LongDouble && RHSKind == BuiltinType::Float)
+ return FloatConvRankCompareResult::FRCR_Greater;
+ if (LHSKind == BuiltinType::Double && RHSKind == BuiltinType::LongDouble)
+ return FloatConvRankCompareResult::FRCR_Lesser;
+ if (LHSKind == BuiltinType::LongDouble && RHSKind == BuiltinType::Double)
+ return FloatConvRankCompareResult::FRCR_Greater;
+
+ const llvm::fltSemantics &LHSSemantics = Ctx.getFloatTypeSemantics(LHS);
+ const llvm::fltSemantics &RHSSemantics = Ctx.getFloatTypeSemantics(RHS);
+
+ bool LHSRepresentableByRHS =
+ llvm::APFloat::isRepresentableBy(LHSSemantics, RHSSemantics);
+ bool RHSRepresentableByLHS =
+ llvm::APFloat::isRepresentableBy(RHSSemantics, LHSSemantics);
+
+ if (LHSRepresentableByRHS && !RHSRepresentableByLHS)
+ return FloatConvRankCompareResult::FRCR_Lesser;
+ if (!LHSRepresentableByRHS && RHSRepresentableByLHS)
+ return FloatConvRankCompareResult::FRCR_Greater;
+
+ if (!LHSRepresentableByRHS && !RHSRepresentableByLHS)
+ return FloatConvRankCompareResult::FRCR_Unordered;
+
+ // Both types are representable by each other, compare sub-ranks, however, as
+ // of today this scenario doesn't exist.
+ llvm_unreachable(
+ "Both types are representable by each other, compare sub-ranks");
+}
/// \returns The locations that are relevant when searching for Doc comments
/// related to \p D.
@@ -7757,7 +7744,7 @@ static FloatingRank getFloatingRank(QualType T) {
/// point types, ignoring the domain of the type (i.e. 'double' ==
/// '_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
+/// LHS < RHS, return FRCR_Lesser. If the values representable 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.
@@ -7775,8 +7762,7 @@ ASTContext::getFloatingTypeOrder(QualType LHS, QualType RHS) const {
RHSKind = CT->getElementType()->castAs<BuiltinType>()->getKind();
else
RHSKind = RHS->castAs<BuiltinType>()->getKind();
- return CXX23FloatingPointConversionRankMap[CXX23FloatRankToIndex(LHSKind)]
- [CXX23FloatRankToIndex(RHSKind)];
+ return CXX23CompareFpConversionRanks(LHSKind, RHSKind, LHS, RHS, *this);
}
FloatingRank LHSR = getFloatingRank(LHS);
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 8a9a93ede77717..dff2e53ff01750 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2304,8 +2304,10 @@ 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;
+ return (BT->getKind() == BuiltinType::Float16 &&
+ Ctx.getTargetInfo().hasFloat16Type()) ||
+ (BT->getKind() == BuiltinType::BFloat16 &&
+ Ctx.getTargetInfo().hasFullBFloat16Type());
if (const auto *CT = dyn_cast<ComplexType>(CanonicalType))
return CT->getElementType()->isCXX23ExtendedFloatingPointType(Ctx);
return false;
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 0b45d0c05eb8ed..e22f4e5de77c98 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -473,8 +473,12 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI,
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");
+ if (TI.hasFloat16Type()) {
+ Builder.defineMacro("__STDCPP_FLOAT16_T__", "1");
+ }
+ if (TI.hasFullBFloat16Type()) {
+ Builder.defineMacro("__STDCPP_BFLOAT16_T__", "1");
+ }
}
// [C++20] The integer literal 202002L.
else if (LangOpts.CPlusPlus20)
diff --git a/clang/lib/Lex/LiteralSupport.cpp b/clang/lib/Lex/LiteralSupport.cpp
index cce05d55b959b0..97965d79903df4 100644
--- a/clang/lib/Lex/LiteralSupport.cpp
+++ b/clang/lib/Lex/LiteralSupport.cpp
@@ -23,6 +23,7 @@
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/Error.h"
@@ -1038,14 +1039,17 @@ NumericLiteralParser::NumericLiteralParser(StringRef TokSpelling,
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;
+ if (HasSize)
+ break;
+ HasSize = true;
+
+ if ((s + 3 < ThisTokEnd) && Target.hasFullBFloat16Type()) {
+ StringRef suffix(s, 4);
+ if (suffix == "bf16" || suffix == "BF16") {
+ s += 3;
+ isBFloat16 = true;
+ continue;
+ }
}
break;
case 'q': // FP Suffix for "__float128"
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 1c7c30c558b60e..8db63f82a74ad2 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -7445,7 +7445,8 @@ isArithmeticArgumentPromotion(Sema &S, const ImplicitCastExpr *ICE) {
if (const auto *VecTy = To->getAs<ExtVectorType>())
To = VecTy->getElementType();
// It's a floating promotion if the source type is float.
- // [7.3.8p1][conv.fpprom] https://eel.is/c++draft/conv.fpprom
+ // [7.3.8p1][conv.fpprom] A prvalue of type float can be converted to a
+ // prvalue of type double. The value is unchanged.
return (ICE->getCastKind() == CK_FloatingCast &&
S.Context.isPromotableFloatingType(From) &&
S.Context.getPromotedFloatingType(From) == To);
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 74dc56b100e67c..5e58f875a14e3d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -4455,7 +4455,7 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc,
? ImplicitConversionSequence::Better
: ImplicitConversionSequence::Worse;
- // C++23 12.2.4.3p4:
+ // C++23 [over.ics.rank]p4b3:
// 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:
diff --git a/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp b/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp
index 171e24a3706543..6f546dc6aeb8ea 100644
--- a/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp
+++ b/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp
@@ -1,5 +1,5 @@
// 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 -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown -target-feature +fullbf16 -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK
// CHECK-LABEL: define {{[^@]+}}@_Z1fDF16b
// CHECK-SAME: (bfloat noundef [[V:%.*]]) #[[ATTR0:[0-9]+]] {
@@ -497,3 +497,67 @@ void test9_7() {
// User-defined overload cases
decltype(0.BF16) bfloat16_val_2(user_defined_val_2); // calls operator int()
}
+
+template <typename T>
+T passThrough(T x) { return x; }
+
+template <typename T>
+void acceptSameType(T, T) {}
+
+// CHECK-LABEL: define {{[^@]+}}@_Z8test10_1v
+// CHECK-SAME: () #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[HF:%.*]] = alloca half, align 2
+// CHECK-NEXT: [[BF:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT: [[RES1:%.*]] = alloca half, align 2
+// CHECK-NEXT: [[RES2:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT: [[F:%.*]] = alloca float, align 4
+// CHECK-NEXT: [[D:%.*]] = alloca double, align 8
+// CHECK-NEXT: [[LD:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: [[R3:%.*]] = alloca float, align 4
+// CHECK-NEXT: [[R4:%.*]] = alloca double, align 8
+// CHECK-NEXT: [[R5:%.*]] = alloca x86_fp80, align 16
+// CHECK-NEXT: store half 0xH3C00, ptr [[HF]], align 2
+// CHECK-NEXT: store bfloat 0xR4000, ptr [[BF]], align 2
+// CHECK-NEXT: [[TMP0:%.*]] = load half, ptr [[HF]], align 2
+// CHECK-NEXT: [[CALL:%.*]] = call noundef half @_Z11passThroughIDF16_ET_S0_(half noundef [[TMP0]])
+// CHECK-NEXT: store half [[CALL]], ptr [[RES1]], align 2
+// CHECK-NEXT: [[TMP1:%.*]] = load bfloat, ptr [[BF]], align 2
+// CHECK-NEXT: [[CALL1:%.*]] = call noundef bfloat @_Z11passThroughIDF16bET_S0_(bfloat noundef [[TMP1]])
+// CHECK-NEXT: store bfloat [[CALL1]], ptr [[RES2]], align 2
+// CHECK-NEXT: store float 3.000000e+00, ptr [[F]], align 4
+// CHECK-NEXT: store double 4.000000e+00, ptr [[D]], align 8
+// CHECK-NEXT: store x86_fp80 0xK4001A000000000000000, ptr [[LD]], align 16
+// CHECK-NEXT: [[TMP2:%.*]] = load half, ptr [[HF]], align 2
+// CHECK-NEXT: call void @_Z14acceptSameTypeIDF16_EvT_S0_(half noundef [[TMP2]], half noundef 0xH4600)
+// CHECK-NEXT: [[TMP3:%.*]] = load bfloat, ptr [[BF]], align 2
+// CHECK-NEXT: call void @_Z14acceptSameTypeIDF16bEvT_S0_(bfloat noundef [[TMP3]], bfloat noundef 0xR40E0)
+// CHECK-NEXT: [[TMP4:%.*]] = load float, ptr [[F]], align 4
+// CHECK-NEXT: [[CALL2:%.*]] = call noundef float @_Z11passThroughIfET_S0_(float noundef [[TMP4]])
+// CHECK-NEXT: store float [[CALL2]], ptr [[R3]], align 4
+// CHECK-NEXT: [[TMP5:%.*]] = load double, ptr [[D]], align 8
+// CHECK-NEXT: [[CALL3:%.*]] = call noundef double @_Z11passThroughIdET_S0_(double noundef [[TMP5]])
+// CHECK-NEXT: store double [[CALL3]], ptr [[R4]], align 8
+// CHECK-NEXT: [[TMP6:%.*]] = load x86_fp80, ptr [[LD]], align 16
+// CHECK-NEXT: [[CALL4:%.*]] = call noundef x86_fp80 @_Z11passThroughIeET_S0_(x86_fp80 noundef [[TMP6]])
+// CHECK-NEXT: store x86_fp80 [[CALL4]], ptr [[R5]], align 16
+// CHECK-NEXT: ret void
+//
+void test10_1() {
+ _Float16 hf = 1.0f16;
+ decltype(0.BF16) bf = 2.0bf16;
+
+ auto res1 = passThrough(hf);
+ auto res2 = passThrough(bf);
+
+ float f = 3.0f;
+ double d = 4.0;
+ long double ld = 5.0L;
+
+ acceptSameType(hf, _Float16(6.0f16));
+ acceptSameType(bf, decltype(0.BF16)(7.0bf16));
+
+ auto r3 = passThrough(f);
+ auto r4 = passThrough(d);
+ auto r5 = passThrough(ld);
+}
\ No newline at end of file
diff --git a/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp b/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp
index 23ac2d21d80777..787d69ca6d7731 100644
--- a/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp
+++ b/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -std=c++23 -target-feature +fullbf16 -verify -ast-dump %s | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++23 -triple x86_64-unknown-unknown -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'}}
diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index bf80fa5a06580b..7ef9db7696ecd4 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -1370,6 +1370,9 @@ class APFloat : public APFloatBase {
/// shorter semantics, like IEEEhalf.
float convertToFloat() const;
+ static bool isRepresentableBy(const fltSemantics &Sem,
+ const fltSemantics &Other);
+
bool operator==(const APFloat &RHS) const { return compare(RHS) == cmpEqual; }
bool operator!=(const APFloat &RHS) const { return compare(RHS) != cmpEqual; }
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index c9adfca8b3b768..7749ced6500a5e 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -5565,6 +5565,16 @@ float APFloat::convertToFloat() const {
return Temp.getIEEE().convertToFloat();
}
+/// Returns true if \p Other can represent all the values that can be
+/// represented by \p Sem.
+///
+/// \param Sem - type float semantics.
+/// \param Other - type float semantics.
+bool APFloat::isRepresentableBy(const fltSemantics &Sem,
+ const fltSemantics &Other) {
+ return Sem.isRepresentableBy(Other);
+}
+
} // namespace llvm
#undef APFLOAT_DISPATCH_ON_SEMANTICS
More information about the llvm-commits
mailing list