Index: clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp +++ clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp @@ -402,7 +402,8 @@ if ((Cast->getCastKind() == CK_NoOp && Context->hasSameType(Cast->getType(), E->getType().withConst())) || (Cast->getCastKind() == CK_LValueToRValue && - !Cast->getType().isNull() && Cast->getType()->isFundamentalType())) + !Cast->getType().isNull() && + Cast->getType()->isFundamentalType(*Context))) return false; } // FIXME: Make this function more generic. @@ -628,7 +629,8 @@ SourceRange ParenRange(Loop->getLParenLoc(), Loop->getRParenLoc()); QualType Type = Context->getAutoDeductType(); - if (!Descriptor.ElemType.isNull() && Descriptor.ElemType->isFundamentalType()) + if (!Descriptor.ElemType.isNull() && + Descriptor.ElemType->isFundamentalType(*Context)) Type = Descriptor.ElemType.getUnqualifiedType(); Type = Type.getDesugaredType(*Context); Index: clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp +++ clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp @@ -410,7 +410,7 @@ return true; // Arithmetic types are interconvertible, except scoped enums. - if (ParamType->isArithmeticType() && ArgType->isArithmeticType()) { + if (ParamType->isArithmeticType(Ctx) && ArgType->isArithmeticType(Ctx)) { if ((ParamType->isEnumeralType() && ParamType->castAs()->getDecl()->isScoped()) || (ArgType->isEnumeralType() && Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -58,6 +58,16 @@ namespace clang { +// Conversion ranks introduced in C++23 6.8.6p2 [conv.rank] +enum FloatingRankCompareResult { + FRCR_Unordered, + FRCR_Lesser, + FRCR_Greater, + FRCR_Equal, + FRCR_Equal_Lesser_Subrank, + FRCR_Equal_Greater_Subrank, +}; + class APValue; class ASTMutationListener; class ASTRecordLayout; @@ -1101,6 +1111,7 @@ SatUnsignedLongFractTy; CanQualType HalfTy; // [OpenCL 6.1.1.1], ARM NEON CanQualType BFloat16Ty; + CanQualType BF16Ty; // [C++23 6.8.3p5], ISO/IEC/IEEE 60559. CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3 CanQualType VoidPtrTy, NullPtrTy; CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy; @@ -2790,14 +2801,21 @@ /// 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. + FloatingRankCompareResult 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). + FloatingRankCompareResult getFloatingTypeSemanticOrder(QualType LHS, + QualType RHS) const; unsigned getTargetAddressSpace(LangAS AS) const; Index: clang/include/clang/AST/BuiltinTypes.def =================================================================== --- clang/include/clang/AST/BuiltinTypes.def +++ clang/include/clang/AST/BuiltinTypes.def @@ -212,6 +212,9 @@ // '_Float16' FLOATING_TYPE(Float16, HalfTy) +// 'Bfloat16' arithmetic type to represent std::bfloat16_t. +FLOATING_TYPE(BF16, BFloat16Ty) + // '__bf16' FLOATING_TYPE(BFloat16, BFloat16Ty) Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -2149,17 +2149,30 @@ 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) + bool isCXX23FloatingPointType() + const; // C++23 6.8.2p12 [basic.fundamental] (standard floating point + + // extended floating point) + bool isCXX23StandardFloatingPointType() + const; // C++23 6.8.2p12 [basic.fundamental] (standard floating point) + bool isCXX23ExtendedFloatingPointType() + const; // C++23 6.8.2p12 [basic.fundamental] (extended floating point) + bool isCXX23StandardAndExtendedFpOp(const ASTContext &Ctx, QualType &Type) + const; // C++23 6.8.2p12 [basic.fundamental] (Used to determine if at + // least one of the types is a standard floating-point type and the + // other is an extended floating-point type) bool isHalfType() const; // OpenCL 6.1.1.1, NEON (IEEE 754-2008 half) bool isFloat16Type() const; // C11 extension ISO/IEC TS 18661 bool isBFloat16Type() const; + bool isBF16Type() const; // [C++23 6.8.3p5], ISO/IEC/IEEE 60559. bool isFloat128Type() const; bool isIbm128Type() const; bool isRealType() const; // C99 6.2.5p17 (real floating + integer) - bool isArithmeticType() const; // C99 6.2.5p18 (integer + floating) + bool isArithmeticType( + const ASTContext &Ctx) const; // C99 6.2.5p18 (integer + floating) bool isVoidType() const; // C99 6.2.5p19 bool isScalarType() const; // C99 6.2.5p21 (arithmetic + pointers) bool isAggregateType() const; - bool isFundamentalType() const; + bool isFundamentalType(const ASTContext &Ctx) const; bool isCompoundType() const; // Type Predicates: Check to see if this type is structurally the specified @@ -6886,12 +6899,11 @@ /// Tests whether the type is categorized as a fundamental type. /// /// \returns True for types specified in C++0x [basic.fundamental]. -inline bool Type::isFundamentalType() const { - return isVoidType() || - isNullPtrType() || +inline bool Type::isFundamentalType(const ASTContext &Ctx) const { + return isVoidType() || isNullPtrType() || // FIXME: It's really annoying that we don't have an // 'isArithmeticType()' which agrees with the standard definition. - (isArithmeticType() && !isEnumeralType()); + (isArithmeticType(Ctx) && !isEnumeralType()); } /// Tests whether the type is categorized as a compound type. @@ -7248,6 +7260,10 @@ return isSpecificBuiltinType(BuiltinType::BFloat16); } +inline bool Type::isBF16Type() const { + return isSpecificBuiltinType(BuiltinType::BF16); +} + inline bool Type::isFloat128Type() const { return isSpecificBuiltinType(BuiltinType::Float128); } Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8743,6 +8743,7 @@ >; def err_cast_to_bfloat16 : Error<"cannot type-cast to __bf16">; def err_cast_from_bfloat16 : Error<"cannot type-cast from __bf16">; +def err_cxx2b_invalid_implicit_floating_point_cast : Error<"floating point cast results in loss of precision">; 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< Index: clang/include/clang/Basic/LangOptions.def =================================================================== --- clang/include/clang/Basic/LangOptions.def +++ clang/include/clang/Basic/LangOptions.def @@ -235,6 +235,7 @@ LANGOPT(OpenCLGenericAddressSpace, 1, 0, "OpenCL generic keyword") LANGOPT(OpenCLPipes , 1, 0, "OpenCL pipes language constructs and built-ins") LANGOPT(NativeHalfType , 1, 0, "Native half type support") +LANGOPT(NativeBFloat16Type , 1, 0, "Native BFloat16 type support") LANGOPT(NativeHalfArgsAndReturns, 1, 0, "Native half args and returns") LANGOPT(CUDA , 1, 0, "CUDA") LANGOPT(HIP , 1, 0, "HIP") Index: clang/include/clang/Basic/TargetInfo.h =================================================================== --- clang/include/clang/Basic/TargetInfo.h +++ clang/include/clang/Basic/TargetInfo.h @@ -760,6 +760,12 @@ llvm_unreachable("bfloat not implemented on this target"); } + /// Return the mangled code of native/arithemetic bfloat16. + virtual const char *getBF16Mangling() const { + llvm_unreachable( + "Arithmetic/Native bfloat16 not implemented on this target"); + } + /// Return the value for the C99 FLT_EVAL_METHOD macro. virtual LangOptions::FPEvalMethodKind getFPEvalMethod() const { return LangOptions::FPEvalMethodKind::FEM_Source; Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -6415,6 +6415,9 @@ HelpText<"Use the native half type for __fp16 instead of promoting to float">, MarshallingInfoFlag>, ImpliedByAnyOf<[open_cl.KeyPath, render_script.KeyPath]>; +def fnative_bfloat16_type: Flag<["-"], "fnative-bfloat16-type">, + HelpText<"Enable bfloat16 arithmetic type in C++23 for targets with native support.">, + MarshallingInfoFlag>; def fnative_half_arguments_and_returns : Flag<["-"], "fnative-half-arguments-and-returns">, HelpText<"Use the native __fp16 type for arguments and returns (and skip ABI-specific lowering)">, MarshallingInfoFlag>, Index: clang/include/clang/Lex/LiteralSupport.h =================================================================== --- clang/include/clang/Lex/LiteralSupport.h +++ clang/include/clang/Lex/LiteralSupport.h @@ -72,6 +72,7 @@ bool isFract : 1; // 1.0hr/r/lr/uhr/ur/ulr bool isAccum : 1; // 1.0hk/k/lk/uhk/uk/ulk bool isBitInt : 1; // 1wb, 1uwb (C2x) + bool isBF16 : 1; // 1.0bf uint8_t MicrosoftInteger; // Microsoft suffix extension i8, i16, i32, or i64. Index: clang/include/clang/Serialization/ASTBitCodes.h =================================================================== --- clang/include/clang/Serialization/ASTBitCodes.h +++ clang/include/clang/Serialization/ASTBitCodes.h @@ -1080,6 +1080,9 @@ /// \brief The '__ibm128' type PREDEF_TYPE_IBM128_ID = 74, + /// \brief The 'bf16' arithmetic type. + PREDEF_TYPE_BF16_ID = 75, + /// OpenCL image types with auto numeration #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ PREDEF_TYPE_##Id##_ID, Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h +++ 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" @@ -601,7 +602,7 @@ // Perform type conversion if ((LTy->isIntegralOrEnumerationType() && RTy->isIntegralOrEnumerationType()) && - (LTy->isArithmeticType() && RTy->isArithmeticType())) { + (LTy->isArithmeticType(Ctx) && RTy->isArithmeticType(Ctx))) { SMTConv::doIntTypeConversion( Solver, Ctx, LHS, LTy, RHS, RTy); return; @@ -775,11 +776,11 @@ // If we have two real floating types, convert the smaller operand to the // bigger result // Note: Safe to skip updating bitwidth because this must terminate - int order = Ctx.getFloatingTypeOrder(LTy, RTy); - if (order > 0) { + FloatingRankCompareResult order = Ctx.getFloatingTypeOrder(LTy, RTy); + if ((order == FRCR_Greater) || (order == FRCR_Equal_Greater_Subrank)) { RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth); RTy = LTy; - } else if (order == 0) { + } else if ((order == FRCR_Equal) || (order == FRCR_Equal_Lesser_Subrank)) { LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth); LTy = RTy; } else { Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -112,6 +112,83 @@ Ibm128Rank }; +constexpr unsigned CXX23FloatRankToIndex(clang::BuiltinType::Kind Kind) { + switch (Kind) { + case clang::BuiltinType::Float16: + return 0; + case clang::BuiltinType::BF16: + return 1; + case clang::BuiltinType::Float: + return 2; + case clang::BuiltinType::Double: + return 3; + case clang::BuiltinType::LongDouble: + return 4; + default: + 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, 5> + CXX23FloatingPointConversionRankMap = { + {// Float16 x Float16 + // Float16 x BFloat16 + // Float16 x Float + // Float16 x Double + // Float16 x LongDouble + {{FloatingRankCompareResult::FRCR_Equal, + FloatingRankCompareResult::FRCR_Unordered, + FloatingRankCompareResult::FRCR_Lesser, + FloatingRankCompareResult::FRCR_Lesser, + FloatingRankCompareResult::FRCR_Lesser}}, + + // BFloat16 x Float16 + // BFloat16 x BFloat16 + // BFloat16 x Float + // BFloat16 x Double + // BFloat16 x LongDouble + {{FloatingRankCompareResult::FRCR_Unordered, + FloatingRankCompareResult::FRCR_Equal, + FloatingRankCompareResult::FRCR_Lesser, + FloatingRankCompareResult::FRCR_Lesser, + FloatingRankCompareResult::FRCR_Lesser}}, + + // Float x Float16 + // Float x BFloat16 + // Float x Float + // Float x Double + // Float x LongDouble + {{FloatingRankCompareResult::FRCR_Greater, + FloatingRankCompareResult::FRCR_Greater, + FloatingRankCompareResult::FRCR_Equal, + FloatingRankCompareResult::FRCR_Lesser, + FloatingRankCompareResult::FRCR_Lesser}}, + + // Double x Float16 + // Double x BFloat16 + // Double x Float + // Double x Double + // Double x LongDouble + {{FloatingRankCompareResult::FRCR_Greater, + FloatingRankCompareResult::FRCR_Greater, + FloatingRankCompareResult::FRCR_Greater, + FloatingRankCompareResult::FRCR_Equal, + FloatingRankCompareResult::FRCR_Lesser}}, + + // LongDouble x Float16 + // LongDouble x BFloat16 + // LongDouble x Float + // LongDouble x Double + // LongDouble x LongDouble + {{FloatingRankCompareResult::FRCR_Greater, + FloatingRankCompareResult::FRCR_Greater, + FloatingRankCompareResult::FRCR_Greater, + FloatingRankCompareResult::FRCR_Greater, + FloatingRankCompareResult::FRCR_Equal}}}}; + /// \returns location that is relevant when searching for Doc comments related /// to \p D. static SourceLocation getDeclLocForCommentSearch(const Decl *D, @@ -1446,6 +1523,8 @@ InitBuiltinType(BFloat16Ty, BuiltinType::BFloat16); + InitBuiltinType(BF16Ty, BuiltinType::BF16); + // Builtin type used to help define __builtin_va_list. VaListTagDecl = nullptr; @@ -1665,6 +1744,7 @@ default: llvm_unreachable("Not a floating point type!"); case BuiltinType::BFloat16: + case BuiltinType::BF16: return Target->getBFloat16Format(); case BuiltinType::Float16: return Target->getHalfFormat(); @@ -2139,6 +2219,10 @@ Align = AuxTarget->getBFloat16Align(); } break; + case BuiltinType::BF16: + Width = Target->getBFloat16Width(); + Align = Target->getBFloat16Align(); + break; case BuiltinType::Float16: case BuiltinType::Half: if (Target->hasFloat16Type() || !getLangOpts().OpenMP || @@ -7066,28 +7150,53 @@ case BuiltinType::LongDouble: return LongDoubleRank; case BuiltinType::Float128: return Float128Rank; case BuiltinType::BFloat16: return BFloat16Rank; + case BuiltinType::BF16: + return BFloat16Rank; case BuiltinType::Ibm128: return Ibm128Rank; } } +/// 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. +FloatingRankCompareResult ASTContext::getFloatingTypeOrder(QualType LHS, + QualType RHS) const { + if (this->LangOpts.CPlusPlus23 && LHS->isCXX23FloatingPointType() && + RHS->isCXX23FloatingPointType()) { + BuiltinType::Kind LHSKind; + BuiltinType::Kind RHSKind; + if (const auto *CT = LHS->getAs()) + LHSKind = CT->getElementType()->castAs()->getKind(); + else + LHSKind = LHS->castAs()->getKind(); + if (const auto *CT = RHS->getAs()) + RHSKind = CT->getElementType()->castAs()->getKind(); + else + RHSKind = RHS->castAs()->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 { +FloatingRankCompareResult +ASTContext::getFloatingTypeSemanticOrder(QualType LHS, QualType RHS) const { if (&getFloatTypeSemantics(LHS) == &getFloatTypeSemantics(RHS)) - return 0; + return FRCR_Equal; return getFloatingTypeOrder(LHS, RHS); } @@ -8053,6 +8162,7 @@ case BuiltinType::NullPtr: return '*'; // like char* case BuiltinType::BFloat16: + case BuiltinType::BF16: case BuiltinType::Float16: case BuiltinType::Float128: case BuiltinType::Ibm128: Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1109,7 +1109,7 @@ if (!DiscardResult && AK == DerefKind::Read) { if (VD->hasLocalStorage() && VD->hasInit() && !VD->isConstexpr()) { QualType VT = VD->getType(); - if (VT.isConstQualified() && VT->isFundamentalType()) + if (VT.isConstQualified() && VT->isFundamentalType(VD->getASTContext())) return this->visit(VD->getInit()); } } Index: clang/lib/AST/ItaniumMangle.cpp =================================================================== --- clang/lib/AST/ItaniumMangle.cpp +++ clang/lib/AST/ItaniumMangle.cpp @@ -3150,6 +3150,9 @@ Out << TI->getBFloat16Mangling(); break; } + case BuiltinType::BF16: + Out << getASTContext().getTargetInfo().getBF16Mangling(); + break; case BuiltinType::Ibm128: { const TargetInfo *TI = &getASTContext().getTargetInfo(); Out << TI->getIbm128Mangling(); Index: clang/lib/AST/MicrosoftMangle.cpp =================================================================== --- clang/lib/AST/MicrosoftMangle.cpp +++ clang/lib/AST/MicrosoftMangle.cpp @@ -2591,7 +2591,8 @@ case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::Ibm128: - case BuiltinType::Float128: { + case BuiltinType::Float128: + case BuiltinType::BF16: { DiagnosticsEngine &Diags = Context.getDiags(); unsigned DiagID = Diags.getCustomDiagID( DiagnosticsEngine::Error, "cannot mangle this built-in %0 type yet"); Index: clang/lib/AST/NSAPI.cpp =================================================================== --- clang/lib/AST/NSAPI.cpp +++ clang/lib/AST/NSAPI.cpp @@ -496,6 +496,7 @@ case BuiltinType::OMPArrayShaping: case BuiltinType::OMPIterator: case BuiltinType::BFloat16: + case BuiltinType::BF16: break; } Index: clang/lib/AST/PrintfFormatString.cpp =================================================================== --- clang/lib/AST/PrintfFormatString.cpp +++ clang/lib/AST/PrintfFormatString.cpp @@ -756,6 +756,7 @@ case BuiltinType::Int128: case BuiltinType::Half: case BuiltinType::BFloat16: + case BuiltinType::BF16: case BuiltinType::Float16: case BuiltinType::Float128: case BuiltinType::Ibm128: Index: clang/lib/AST/StmtPrinter.cpp =================================================================== --- clang/lib/AST/StmtPrinter.cpp +++ clang/lib/AST/StmtPrinter.cpp @@ -1348,6 +1348,9 @@ case BuiltinType::Float: OS << 'F'; break; case BuiltinType::LongDouble: OS << 'L'; break; case BuiltinType::Float128: OS << 'Q'; break; + case BuiltinType::BF16: + OS << "BF16"; + break; } } Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -2157,6 +2157,36 @@ return false; } +bool Type::isCXX23FloatingPointType() const { + return isCXX23StandardFloatingPointType() || + isCXX23ExtendedFloatingPointType(); +} + +bool Type::isCXX23StandardFloatingPointType() const { + if (const auto *BT = dyn_cast(CanonicalType)) + return BT->getKind() >= BuiltinType::Float && + BT->getKind() <= BuiltinType::LongDouble; + if (const auto *CT = dyn_cast(CanonicalType)) + return CT->getElementType()->isCXX23StandardFloatingPointType(); + return false; +} + +bool Type::isCXX23ExtendedFloatingPointType() const { + if (const auto *BT = dyn_cast(CanonicalType)) + return BT->getKind() == BuiltinType::Float16 || + BT->getKind() == BuiltinType::BF16; + if (const auto *CT = dyn_cast(CanonicalType)) + return CT->getElementType()->isCXX23ExtendedFloatingPointType(); + return false; +} +bool Type::isCXX23StandardAndExtendedFpOp(const ASTContext &Ctx, + QualType &Type) const { + return (Ctx.getLangOpts().CPlusPlus23 && + ((isCXX23FloatingPointType() && Type->isCXX23FloatingPointType())) && + (isCXX23ExtendedFloatingPointType() || + Type->isCXX23ExtendedFloatingPointType())); +} + bool Type::hasFloatingRepresentation() const { if (const auto *VT = dyn_cast(CanonicalType)) return VT->getElementType()->isFloatingType(); @@ -2180,11 +2210,14 @@ return isBitIntType(); } -bool Type::isArithmeticType() const { +bool Type::isArithmeticType(const ASTContext &Ctx) const { if (const auto *BT = dyn_cast(CanonicalType)) - return BT->getKind() >= BuiltinType::Bool && - BT->getKind() <= BuiltinType::Ibm128 && - BT->getKind() != BuiltinType::BFloat16; + return (BT->getKind() >= BuiltinType::Bool && + BT->getKind() <= BuiltinType::Ibm128 && + BT->getKind() != BuiltinType::BFloat16 && + BT->getKind() != BuiltinType::BF16) || + (Ctx.getLangOpts().CPlusPlus23 && + BT->getKind() == BuiltinType::BF16); if (const auto *ET = dyn_cast(CanonicalType)) // GCC allows forward declaration of enum types (forbid by C99 6.7.2.3p2). // If a body isn't seen by the time we get here, return false. @@ -3123,6 +3156,8 @@ return Policy.Half ? "half" : "__fp16"; case BFloat16: return "__bf16"; + case BF16: + return "BF16"; case Float: return "float"; case Double: Index: clang/lib/AST/TypeLoc.cpp =================================================================== --- clang/lib/AST/TypeLoc.cpp +++ clang/lib/AST/TypeLoc.cpp @@ -392,6 +392,7 @@ case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: + case BuiltinType::BF16: llvm_unreachable("Builtin type needs extra local data!"); // Fall through, if the impossible happens. Index: clang/lib/Basic/Targets/AArch64.h =================================================================== --- clang/lib/Basic/Targets/AArch64.h +++ clang/lib/Basic/Targets/AArch64.h @@ -190,6 +190,9 @@ int getEHDataRegisterNumber(unsigned RegNo) const override; const char *getBFloat16Mangling() const override { return "u6__bf16"; }; + + const char *getBF16Mangling() const override { return "u6__bf16"; }; + bool hasInt128Type() const override; bool hasBitIntType() const override { return true; } Index: clang/lib/Basic/Targets/AMDGPU.h =================================================================== --- clang/lib/Basic/Targets/AMDGPU.h +++ clang/lib/Basic/Targets/AMDGPU.h @@ -116,7 +116,7 @@ bool hasBFloat16Type() const override { return isAMDGCN(getTriple()); } const char *getBFloat16Mangling() const override { return "u6__bf16"; }; - + const char *getBF16Mangling() const override { return "DF16b"; }; std::string_view getClobbers() const override { return ""; } ArrayRef getGCCRegNames() const override; Index: clang/lib/Basic/Targets/ARM.h =================================================================== --- clang/lib/Basic/Targets/ARM.h +++ clang/lib/Basic/Targets/ARM.h @@ -197,6 +197,8 @@ bool hasBitIntType() const override { return true; } const char *getBFloat16Mangling() const override { return "u6__bf16"; }; + + const char *getBF16Mangling() const override { return "u6__bf16"; }; }; class LLVM_LIBRARY_VISIBILITY ARMleTargetInfo : public ARMTargetInfo { Index: clang/lib/Basic/Targets/NVPTX.h =================================================================== --- clang/lib/Basic/Targets/NVPTX.h +++ clang/lib/Basic/Targets/NVPTX.h @@ -182,6 +182,7 @@ bool hasBitIntType() const override { return true; } bool hasBFloat16Type() const override { return true; } const char *getBFloat16Mangling() const override { return "u6__bf16"; }; + const char *getBF16Mangling() const override { return "DF16b"; }; }; } // namespace targets } // namespace clang Index: clang/lib/Basic/Targets/X86.h =================================================================== --- clang/lib/Basic/Targets/X86.h +++ clang/lib/Basic/Targets/X86.h @@ -418,6 +418,8 @@ } const char *getBFloat16Mangling() const override { return "u6__bf16"; }; + + const char *getBF16Mangling() const override { return "DF16b"; }; }; // X86-32 generic target Index: clang/lib/CodeGen/CGDebugInfo.cpp =================================================================== --- clang/lib/CodeGen/CGDebugInfo.cpp +++ clang/lib/CodeGen/CGDebugInfo.cpp @@ -884,6 +884,7 @@ case BuiltinType::LongDouble: case BuiltinType::Float16: case BuiltinType::BFloat16: + case BuiltinType::BF16: case BuiltinType::Float128: case BuiltinType::Double: case BuiltinType::Ibm128: Index: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -3000,7 +3000,7 @@ // __imag can only produce an rvalue on scalars. if (E->getOpcode() == UO_Real && !LV.getAddress(*this).getElementType()->isStructTy()) { - assert(E->getSubExpr()->getType()->isArithmeticType()); + assert(E->getSubExpr()->getType()->isArithmeticType(getContext())); return LV; } Index: clang/lib/CodeGen/CGExprScalar.cpp =================================================================== --- clang/lib/CodeGen/CGExprScalar.cpp +++ clang/lib/CodeGen/CGExprScalar.cpp @@ -3445,7 +3445,8 @@ assert( isa(BO->getLHS()->getType().getCanonicalType()) && "first operand must be a matrix"); - assert(BO->getRHS()->getType().getCanonicalType()->isArithmeticType() && + assert(BO->getRHS()->getType().getCanonicalType()->isArithmeticType( + CGF.getContext()) && "second operand must be an arithmetic type"); CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, Ops.FPFeatures); return MB.CreateScalarDiv(Ops.LHS, Ops.RHS, Index: clang/lib/CodeGen/CodeGenTypes.cpp =================================================================== --- clang/lib/CodeGen/CodeGenTypes.cpp +++ clang/lib/CodeGen/CodeGenTypes.cpp @@ -530,6 +530,7 @@ !Context.getTargetInfo().useFP16ConversionIntrinsics()); break; case BuiltinType::BFloat16: + case BuiltinType::BF16: case BuiltinType::Float: case BuiltinType::Double: case BuiltinType::LongDouble: Index: clang/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- clang/lib/CodeGen/ItaniumCXXABI.cpp +++ clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3317,6 +3317,7 @@ case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: + case BuiltinType::BF16: return false; case BuiltinType::Dependent: Index: clang/lib/Frontend/InitPreprocessor.cpp =================================================================== --- clang/lib/Frontend/InitPreprocessor.cpp +++ clang/lib/Frontend/InitPreprocessor.cpp @@ -452,8 +452,13 @@ } else { // -- __cplusplus // FIXME: Use correct value for C++23. - if (LangOpts.CPlusPlus23) + if (LangOpts.CPlusPlus23) { Builder.defineMacro("__cplusplus", "202101L"); + // [C++23] 15.11p2 [cpp.predefined] + Builder.defineMacro("__STDCPP_FLOAT16_T__", "1"); + if (LangOpts.NativeBFloat16Type) + Builder.defineMacro("__STDCPP_BFLOAT16_T__", "1"); + } // [C++20] The integer literal 202002L. else if (LangOpts.CPlusPlus20) Builder.defineMacro("__cplusplus", "202002L"); Index: clang/lib/Index/USRGeneration.cpp =================================================================== --- clang/lib/Index/USRGeneration.cpp +++ clang/lib/Index/USRGeneration.cpp @@ -795,6 +795,7 @@ case BuiltinType::SatULongFract: Out << "@BT@SatULongFract"; break; case BuiltinType::BFloat16: + case BuiltinType::BF16: Out << "@BT@__bf16"; break; case BuiltinType::Ibm128: Out << "@BT@__ibm128"; break; Index: clang/lib/Lex/LiteralSupport.cpp =================================================================== --- clang/lib/Lex/LiteralSupport.cpp +++ clang/lib/Lex/LiteralSupport.cpp @@ -858,6 +858,7 @@ isAccum = false; hadError = false; isBitInt = false; + isBF16 = 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 @@ -958,6 +959,23 @@ 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 (!LangOpts.CPlusPlus23 || !LangOpts.NativeBFloat16Type) + break; + if (HasSize) + break; + HasSize = true; + s += 3; + isBF16 = true; + continue; + } + break; case 'q': // FP Suffix for "__float128" case 'Q': if (!isFPConstant) break; // Error for integer constant. @@ -1090,6 +1108,7 @@ saw_fixed_point_suffix = false; isFract = false; isAccum = false; + isBF16 = false; } saw_ud_suffix = true; Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -694,6 +694,23 @@ } } + // 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 (ExprTy->isCXX23StandardAndExtendedFpOp(Context, TypeTy) && + Kind == CK_FloatingCast && E->isPRValue() && + (CCK == CCK_ImplicitConversion)) { + auto conversionRank = Context.getFloatingTypeOrder(TypeTy, ExprTy); + if (conversionRank < FRCR_Greater) { + Diag(E->getExprLoc(), + diag::err_cxx2b_invalid_implicit_floating_point_cast) + << E->getSourceRange(); + return ExprError(); + } + } + if (ImplicitCastExpr *ImpCast = dyn_cast(E)) { if (ImpCast->getCastKind() == Kind && (!BasePath || BasePath->empty())) { ImpCast->setType(Ty); Index: clang/lib/Sema/SemaCast.cpp =================================================================== --- clang/lib/Sema/SemaCast.cpp +++ clang/lib/Sema/SemaCast.cpp @@ -1359,6 +1359,19 @@ } } + // [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() && + DestType->isCXX23StandardAndExtendedFpOp(Self.Context, SrcType)) { + // Support to cast between fp16 and bf16 doesn't exist yet. + if (!((DestType->isBF16Type() || DestType->isFloat16Type()) && + (SrcType->isBF16Type() || 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). @@ -3108,8 +3121,9 @@ // If either type is a pointer, the other type has to be either an // integer or a pointer. - if (!DestType->isArithmeticType()) { - if (!SrcType->isIntegralType(Self.Context) && SrcType->isArithmeticType()) { + if (!DestType->isArithmeticType(Self.Context)) { + if (!SrcType->isIntegralType(Self.Context) && + SrcType->isArithmeticType(Self.Context)) { Self.Diag(SrcExpr.get()->getExprLoc(), diag::err_cast_pointer_from_non_pointer_int) << SrcType << SrcExpr.get()->getSourceRange(); @@ -3118,9 +3132,9 @@ } checkIntToPointerCast(/* CStyle */ true, OpRange, SrcExpr.get(), DestType, Self); - } else if (!SrcType->isArithmeticType()) { + } else if (!SrcType->isArithmeticType(Self.Context)) { if (!DestType->isIntegralType(Self.Context) && - DestType->isArithmeticType()) { + DestType->isArithmeticType(Self.Context)) { Self.Diag(SrcExpr.get()->getBeginLoc(), diag::err_cast_pointer_to_non_pointer_int) << DestType << SrcExpr.get()->getSourceRange(); Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -10531,7 +10531,7 @@ 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; + S.Context.getFloatingTypeOrder(From, To) == FRCR_Lesser; } bool @@ -13821,9 +13821,12 @@ 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())) + FloatingRankCompareResult 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); @@ -14249,7 +14252,7 @@ int Order = S.getASTContext().getFloatingTypeSemanticOrder( QualType(SourceBT, 0), QualType(TargetBT, 0)); - if (Order > 0) { + if (Order == FRCR_Greater) { // Don't warn about float constants that are precisely // representable in the target type. Expr::EvalResult result; @@ -14267,7 +14270,7 @@ DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_float_precision); } // ... or possibly if we're increasing rank, too - else if (Order < 0) { + else if (Order == FRCR_Lesser) { if (S.SourceMgr.isInSystemMacro(CC)) return; Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -795,7 +795,7 @@ 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, @@ -804,7 +804,8 @@ : 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( @@ -1155,14 +1156,16 @@ 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) + FloatingRankCompareResult 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 @@ -1215,16 +1218,34 @@ 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) { + FloatingRankCompareResult 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; @@ -1561,7 +1582,8 @@ // If either side is a non-arithmetic type (e.g. a pointer), we are done. // The caller can deal with this (e.g. pointer + int). - if (!LHSType->isArithmeticType() || !RHSType->isArithmeticType()) + if (!LHSType->isArithmeticType(Context) || + !RHSType->isArithmeticType(Context)) return QualType(); // Apply unary and bitfield promotions to the LHS's type. @@ -3934,6 +3956,8 @@ Ty = Context.Float16Ty; else if (Literal.isFloat128) Ty = Context.Float128Ty; + else if (Literal.isBF16) + Ty = Context.BF16Ty; else Ty = Context.DoubleTy; @@ -4181,7 +4205,8 @@ // scalar or vector data type argument..." // Every built-in scalar type (OpenCL 1.1 6.1.1) is either an arithmetic // type (C99 6.2.5p18) or void. - if (!(T->isArithmeticType() || T->isVoidType() || T->isVectorType())) { + if (!(T->isArithmeticType(S.Context) || T->isVoidType() || + T->isVectorType())) { S.Diag(Loc, diag::err_vecstep_non_scalar_vector_type) << T << ArgRange; return true; @@ -4726,7 +4751,7 @@ return CT->getElementType(); // Otherwise they pass through real integer and floating point types here. - if (V.get()->getType()->isArithmeticType()) + if (V.get()->getType()->isArithmeticType(S.Context)) return V.get()->getType(); // Test for placeholders. @@ -8973,7 +8998,7 @@ // If both operands have arithmetic type, do the usual arithmetic conversions // to find a common type: C99 6.5.15p3,5. - if (LHSTy->isArithmeticType() && RHSTy->isArithmeticType()) { + if (LHSTy->isArithmeticType(Context) && RHSTy->isArithmeticType(Context)) { // Disallow invalid arithmetic conversions, such as those between bit- // precise integers types of different sizes, or between a bit-precise // integer and another type. @@ -9874,7 +9899,7 @@ if (LHSType->isExtVectorType()) { if (RHSType->isExtVectorType()) return Incompatible; - if (RHSType->isArithmeticType()) { + if (RHSType->isArithmeticType(Context)) { // CK_VectorSplat does T -> vector T, so first cast to the element type. if (ConvertRHS) RHS = prepareVectorSplat(LHSType, RHS.get()); @@ -9965,7 +9990,8 @@ return Incompatible; // Arithmetic conversions. - if (LHSType->isArithmeticType() && RHSType->isArithmeticType() && + if (LHSType->isArithmeticType(Context) && + RHSType->isArithmeticType(Context) && !(getLangOpts().CPlusPlus && LHSType->isEnumeralType())) { if (ConvertRHS) Kind = PrepareScalarCast(RHS, LHSType); @@ -10540,7 +10566,8 @@ } 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; } @@ -10687,7 +10714,8 @@ // Reject cases where the vector element type or the scalar element type are // not integral or floating point types. - if (!VectorEltTy->isArithmeticType() || !ScalarTy->isArithmeticType()) + if (!VectorEltTy->isArithmeticType(S.Context) || + !ScalarTy->isArithmeticType(S.Context)) return true; // The conversion to apply to the scalar before splatting it, @@ -10728,8 +10756,19 @@ // 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) + FloatingRankCompareResult 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.getLangOpts().CPlusPlus23 && + (ScalarTy->isCXX23StandardAndExtendedFpOp(S.Context, VectorEltTy)) && + (Order <= FRCR_Lesser)) + return true; + + if (!CstScalar && (Order == FRCR_Lesser)) return true; // If the scalar cannot be safely casted to the vector element type, @@ -11212,7 +11251,8 @@ return CheckMatrixMultiplyOperands(LHS, RHS, Loc, IsCompAssign); // For division, only matrix-by-scalar is supported. Other combinations with // matrix types are invalid. - if (IsDiv && LHSTy->isConstantMatrixType() && RHSTy->isArithmeticType()) + if (IsDiv && LHSTy->isConstantMatrixType() && + RHSTy->isArithmeticType(Context)) return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); QualType compType = UsualArithmeticConversions( @@ -11220,8 +11260,7 @@ if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); - - if (compType.isNull() || !compType->isArithmeticType()) + if (compType.isNull() || !compType->isArithmeticType(Context)) return InvalidOperands(Loc, LHS, RHS); if (IsDiv) { DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv); @@ -11595,7 +11634,7 @@ } // handle the common case first (both operands are arithmetic). - if (!compType.isNull() && compType->isArithmeticType()) { + if (!compType.isNull() && compType->isArithmeticType(Context)) { if (CompLHSTy) *CompLHSTy = compType; return compType; } @@ -11706,7 +11745,7 @@ // Enforce type constraints: C99 6.5.6p3. // Handle the common case first (both operands are arithmetic). - if (!compType.isNull() && compType->isArithmeticType()) { + if (!compType.isNull() && compType->isArithmeticType(Context)) { if (CompLHSTy) *CompLHSTy = compType; return compType; } @@ -12667,7 +12706,7 @@ } QualType IntType = LHSStrippedType->castAs()->getDecl()->getIntegerType(); - assert(IntType->isArithmeticType()); + assert(IntType->isArithmeticType(S.Context)); // We can't use `CK_IntegralCast` when the underlying type is 'bool', so we // promote the boolean type, and all other promotable integer types, to @@ -12721,7 +12760,7 @@ return QualType(); if (Type.isNull()) return S.InvalidOperands(Loc, LHS, RHS); - assert(Type->isArithmeticType() || Type->isEnumeralType()); + assert(Type->isArithmeticType(S.Context) || Type->isEnumeralType()); if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc)) return S.InvalidOperands(Loc, LHS, RHS); @@ -12813,8 +12852,8 @@ QualType LHSType = LHS.get()->getType(); QualType RHSType = RHS.get()->getType(); - if ((LHSType->isArithmeticType() || LHSType->isEnumeralType()) && - (RHSType->isArithmeticType() || RHSType->isEnumeralType())) + if ((LHSType->isArithmeticType(Context) || LHSType->isEnumeralType()) && + (RHSType->isArithmeticType(Context) || RHSType->isEnumeralType())) return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc); const Expr::NullPointerConstantKind LHSNullKind = @@ -16015,7 +16054,7 @@ resultType = Input.get()->getType(); if (resultType->isDependentType()) break; - if (resultType->isArithmeticType()) // C99 6.5.3.3p1 + if (resultType->isArithmeticType(Context)) // C99 6.5.3.3p1 break; else if (resultType->isVectorType() && // The z vector extensions don't allow + or - with bool vectors. Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -5004,9 +5004,9 @@ case UTT_IsReference: return T->isReferenceType(); case UTT_IsArithmetic: - return T->isArithmeticType() && !T->isEnumeralType(); + return T->isArithmeticType(C) && !T->isEnumeralType(); case UTT_IsFundamental: - return T->isFundamentalType(); + return T->isFundamentalType(C); case UTT_IsObject: return T->isObjectType(); case UTT_IsScalar: @@ -6712,7 +6712,7 @@ // -- The second and third operands have arithmetic or enumeration type; // the usual arithmetic conversions are performed to bring them to a // common type, and the result is of that type. - if (LTy->isArithmeticType() && RTy->isArithmeticType()) { + if (LTy->isArithmeticType(Context) && RTy->isArithmeticType(Context)) { QualType ResTy = UsualArithmeticConversions(LHS, RHS, QuestionLoc, ACK_Conditional); if (LHS.isInvalid() || RHS.isInvalid()) Index: clang/lib/Sema/SemaObjCProperty.cpp =================================================================== --- clang/lib/Sema/SemaObjCProperty.cpp +++ clang/lib/Sema/SemaObjCProperty.cpp @@ -1386,8 +1386,7 @@ // specifically for property redeclarations as well as for ivars. QualType lhsType =Context.getCanonicalType(PropertyIvarType).getUnqualifiedType(); QualType rhsType =Context.getCanonicalType(IvarType).getUnqualifiedType(); - if (lhsType != rhsType && - lhsType->isArithmeticType()) { + if (lhsType != rhsType && lhsType->isArithmeticType(Context)) { Diag(PropertyDiagLoc, diag::err_property_ivar_type) << property->getDeclName() << PropType << Ivar->getDeclName() << IvarType; @@ -1733,7 +1732,7 @@ compat = true; QualType lhsType = Context.getCanonicalType(PropertyRValueType); QualType rhsType =Context.getCanonicalType(GetterType).getUnqualifiedType(); - if (lhsType != rhsType && lhsType->isArithmeticType()) + if (lhsType != rhsType && lhsType->isArithmeticType(Context)) compat = false; } } Index: clang/lib/Sema/SemaOpenMP.cpp =================================================================== --- clang/lib/Sema/SemaOpenMP.cpp +++ clang/lib/Sema/SemaOpenMP.cpp @@ -19366,7 +19366,7 @@ if (DeclareReductionRef.isUnset()) { if ((BOK == BO_GT || BOK == BO_LT) && !(Type->isScalarType() || - (S.getLangOpts().CPlusPlus && Type->isArithmeticType()))) { + (S.getLangOpts().CPlusPlus && Type->isArithmeticType(Context)))) { S.Diag(ELoc, diag::err_omp_clause_not_arithmetic_type_arg) << getOpenMPClauseName(ClauseKind) << S.getLangOpts().CPlusPlus; if (!ASE && !OASE) { Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -397,7 +397,7 @@ // 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); @@ -1750,7 +1750,7 @@ return false; // Vector splat from any arithmetic type to a vector. - if (FromType->isArithmeticType()) { + if (FromType->isArithmeticType(S.Context)) { ICK = ICK_Vector_Splat; return true; } @@ -1969,9 +1969,8 @@ SCS.Second = ICK_Complex_Promotion; FromType = ToType.getUnqualifiedType(); } else if (ToType->isBooleanType() && - (FromType->isArithmeticType() || - FromType->isAnyPointerType() || - FromType->isBlockPointerType() || + (FromType->isArithmeticType(S.Context) || + FromType->isAnyPointerType() || FromType->isBlockPointerType() || FromType->isMemberPointerType())) { // Boolean conversions (C++ 4.12). SCS.Second = ICK_Boolean_Conversion; @@ -1985,8 +1984,10 @@ // Complex conversions (C99 6.3.1.6) SCS.Second = ICK_Complex_Conversion; FromType = ToType.getUnqualifiedType(); - } else if ((FromType->isAnyComplexType() && ToType->isArithmeticType()) || - (ToType->isAnyComplexType() && FromType->isArithmeticType())) { + } else if ((FromType->isAnyComplexType() && + ToType->isArithmeticType(S.Context)) || + (ToType->isAnyComplexType() && + FromType->isArithmeticType(S.Context))) { // Complex-real conversions (C99 6.3.1.7) SCS.Second = ICK_Complex_Real; FromType = ToType.getUnqualifiedType(); @@ -1995,8 +1996,17 @@ // if their representation is different until there is back end support // We of course allow this conversion if long double is really double. + // 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 ((FromType->isCXX23StandardAndExtendedFpOp(S.Context, ToType)) && + (S.Context.getFloatingTypeOrder(ToType, FromType) <= FRCR_Lesser)) + return false; + // Conversions between bfloat and other floats are not permitted. - if (FromType == S.Context.BFloat16Ty || ToType == S.Context.BFloat16Ty) + if (FromType->isBFloat16Type() || ToType->isBFloat16Type()) return false; // Conversions between IEEE-quad and IBM-extended semantics are not @@ -2018,7 +2028,7 @@ (FromType->isIntegralOrUnscopedEnumerationType() && ToType->isRealFloatingType())) { // Conversions between bfloat and int are not permitted. - if (FromType->isBFloat16Type() || ToType->isBFloat16Type()) + if ((FromType->isBFloat16Type() || ToType->isBFloat16Type())) return false; // Floating-integral conversions (C++ 4.9). @@ -2048,9 +2058,8 @@ // Compatible conversions (Clang extension for C function overloading) SCS.Second = ICK_Compatible_Conversion; FromType = ToType.getUnqualifiedType(); - } else if (IsTransparentUnionStandardConversion(S, From, ToType, - InOverloadResolution, - SCS, CStyle)) { + } else if (IsTransparentUnionStandardConversion( + S, From, ToType, InOverloadResolution, SCS, CStyle)) { SCS.Second = ICK_TransparentUnionConversion; FromType = ToType; } else if (tryAtomicConversion(S, From, ToType, InOverloadResolution, SCS, @@ -4166,6 +4175,74 @@ ? 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 (S.Context.hasSameType(SCS1.getFromType(), SCS2.getFromType())) { + + if ((SCS1.Second == ICK_Floating_Conversion) && + (SCS1.getFromType()->isCXX23FloatingPointType() && + SCS1.getToType(1)->isCXX23FloatingPointType()) && + S.Context.getFloatingTypeOrder(SCS1.getToType(1), + SCS1.getFromType()) >= FRCR_Equal) { + + if ((SCS2.getToType(1)->isArithmeticType(S.Context)) && + SCS2.getFromType()->isCXX23FloatingPointType()) { + + if (!SCS2.getToType(1)->isCXX23FloatingPointType()) { + return ImplicitConversionSequence::Better; + } + + if (S.Context.getFloatingTypeOrder(SCS2.getToType(1), + SCS1.getFromType()) < FRCR_Equal) { + 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.getFromType()->isCXX23FloatingPointType() && + SCS2.getToType(1)->isCXX23FloatingPointType()) && + S.Context.getFloatingTypeOrder(SCS2.getToType(1), + SCS2.getFromType()) >= FRCR_Equal) { + if ((SCS1.getToType(1)->isArithmeticType(S.Context)) && + SCS1.getFromType()->isCXX23FloatingPointType()) { + + if (!SCS1.getToType(1)->isCXX23FloatingPointType()) { + return ImplicitConversionSequence::Worse; + } + + if (S.Context.getFloatingTypeOrder(SCS1.getToType(1), + SCS2.getFromType()) < FRCR_Equal) { + 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, @@ -8253,7 +8330,7 @@ // Flag if we encounter an arithmetic type. HasArithmeticOrEnumeralTypes = - HasArithmeticOrEnumeralTypes || Ty->isArithmeticType(); + HasArithmeticOrEnumeralTypes || Ty->isArithmeticType(Context); if (Ty->isObjCIdType() || Ty->isObjCClassType()) PointerTypes.insert(Ty); Index: clang/lib/Serialization/ASTCommon.cpp =================================================================== --- clang/lib/Serialization/ASTCommon.cpp +++ clang/lib/Serialization/ASTCommon.cpp @@ -273,6 +273,9 @@ case BuiltinType::BFloat16: ID = PREDEF_TYPE_BFLOAT16_ID; break; + case BuiltinType::BF16: + ID = PREDEF_TYPE_BF16_ID; + break; } return TypeIdx(ID); Index: clang/lib/Serialization/ASTReader.cpp =================================================================== --- clang/lib/Serialization/ASTReader.cpp +++ clang/lib/Serialization/ASTReader.cpp @@ -7024,6 +7024,9 @@ case PREDEF_TYPE_BFLOAT16_ID: T = Context.BFloat16Ty; break; + case PREDEF_TYPE_BF16_ID: + T = Context.BF16Ty; + break; case PREDEF_TYPE_HALF_ID: T = Context.HalfTy; break; Index: clang/test/CodeGenCXX/cxx2b-fp-ext-std-names-p1467r9.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/cxx2b-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 -fnative-bfloat16-type -S -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +// CHECK-LABEL: define {{[^@]+}}@_Z1fDF16b +// CHECK-SAME: (bfloat noundef [[V:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: [[V_ADDR:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: store bfloat [[V]], ptr [[V_ADDR]], align 2 +// CHECK-NEXT: ret i32 1 +// +int f(decltype(0.BF16) v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1fDF16_ +// CHECK-SAME: (half noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca half, align 2 +// CHECK-NEXT: store half [[V]], ptr [[V_ADDR]], align 2 +// CHECK-NEXT: ret i32 1 +// +int f(_Float16 v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1ff +// CHECK-SAME: (float noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca float, align 4 +// CHECK-NEXT: store float [[V]], ptr [[V_ADDR]], align 4 +// CHECK-NEXT: ret i32 1 +// +int f(float v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1fd +// CHECK-SAME: (double noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca double, align 8 +// CHECK-NEXT: store double [[V]], ptr [[V_ADDR]], align 8 +// CHECK-NEXT: ret i32 1 +// +int f(double v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1fe +// CHECK-SAME: (x86_fp80 noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca x86_fp80, align 16 +// CHECK-NEXT: store x86_fp80 [[V]], ptr [[V_ADDR]], align 16 +// CHECK-NEXT: ret i32 1 +// +int f(long double v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1fi +// CHECK-SAME: (i32 noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[V]], ptr [[V_ADDR]], align 4 +// CHECK-NEXT: ret i32 1 +// +int f(int v) { return 1; } + +// CHECK-LABEL: define {{[^@]+}}@_Z3f_2e +// CHECK-SAME: (x86_fp80 noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca x86_fp80, align 16 +// CHECK-NEXT: store x86_fp80 [[V]], ptr [[V_ADDR]], align 16 +// CHECK-NEXT: ret i32 1 +// +int f_2(long double v) { return 1; } + +struct S { + + operator decltype(0.BF16)() const { + return 0.0bf16; + } + + operator _Float16() const { + return 0.0f16; + } + + operator float() const { + return 0.0f; + } + + operator double() const { + return 0.0; + } + + operator long double() const { + return 0.0L; + } + + operator int() const { + return 0; + } +}; + +struct S1 { + operator _Float16() const { + return 0.0f16; + } + operator float() const { + return 0.0f; + } + operator double() const { + return 0.0; + } + operator long double() const { + return 0.0L; + } + + operator int() const { + return 0; + } +}; + +// CHECK-LABEL: define {{[^@]+}}@_Z5test1v +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: [[F16_VAL_6:%.*]] = alloca half, align 2 +// CHECK-NEXT: [[F16_VAL_8:%.*]] = alloca half, align 2 +// CHECK-NEXT: [[F16_VAL_9:%.*]] = alloca half, align 2 +// CHECK-NEXT: [[F16_VAL_10:%.*]] = alloca half, align 2 +// CHECK-NEXT: [[F16_VAL_11:%.*]] = alloca half, align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_6]], align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_8]], align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_9]], align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_10]], align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_11]], align 2 +// CHECK-NEXT: ret void +// +void test1() { + _Float16 f16_val_6 = 1.0f16; + _Float16 f16_val_8 = static_cast<_Float16>(1.0f); + _Float16 f16_val_9 = static_cast<_Float16>(1.0); + _Float16 f16_val_10 = static_cast<_Float16>(1.0l); + _Float16 f16_val_11 = static_cast<_Float16>(1.0f16); +} + +// CHECK-LABEL: define {{[^@]+}}@_Z5test2v +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: [[BF16_VAL_5:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: [[BF16_VAL_7:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: [[BF16_VAL_8:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: [[BF16_VAL_9:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: [[BF16_VAL_10:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_5]], align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_7]], align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_8]], align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_9]], align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_10]], align 2 +// CHECK-NEXT: ret void +// +void test2() { + decltype(0.BF16) bf16_val_5 = 1.0bf16; + decltype(0.BF16) bf16_val_7 = static_cast(1.0f); + decltype(0.BF16) bf16_val_8 = static_cast(1.0); + decltype(0.BF16) bf16_val_9 = static_cast(1.0l); + decltype(0.BF16) bf16_val_10 = static_cast(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() +} Index: clang/test/Sema/cxx2b-fp-ext-std-names-p1467r9.cpp =================================================================== --- /dev/null +++ clang/test/Sema/cxx2b-fp-ext-std-names-p1467r9.cpp @@ -0,0 +1,465 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++23 -fnative-bfloat16-type -verify -ast-dump %s | FileCheck %s + +_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> +//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> +//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> +//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> +//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(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(1.0f); +//CHECK: VarDecl {{.*}} bf16_val_7 'decltype(0.BF16)':'BF16' cinit +//CHECK-NEXT: CXXStaticCastExpr {{.*}} 'decltype(0.BF16)':'BF16' static_cast +//CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.000000e+00 +decltype(0.0BF16) bf16_val_8 = static_cast(1.0); +//CHECK: VarDecl {{.*}} bf16_val_8 'decltype(0.BF16)':'BF16' cinit +//CHECK-NEXT: CXXStaticCastExpr {{.*}} 'decltype(0.BF16)':'BF16' static_cast +//CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00 +decltype(0.0BF16) bf16_val_9 = static_cast(1.0l); +//CHECK: VarDecl {{.*}} bf16_val_9 'decltype(0.BF16)':'BF16' cinit +//CHECK-NEXT: CXXStaticCastExpr {{.*}} 'decltype(0.BF16)':'BF16' static_cast +//CHECK-NEXT: FloatingLiteral {{.*}} 'long double' 1.000000e+00 +decltype(0.0BF16) bf16_val_10 = static_cast(1.0bf16); +//CHECK: VarDecl {{.*}} bf16_val_10 'decltype(0.BF16)':'BF16' cinit +//CHECK-NEXT: CXXStaticCastExpr {{.*}} 'decltype(0.BF16)':'BF16' static_cast +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 + +float f_val_1 = 1.0f16; +//CHECK: VarDecl {{.*}} f_val_1 'float' cinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00 +float f_val_2 = 1.0bf16; +//CHECK: VarDecl {{.*}} f_val_2 'float' cinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 +float f_val_3 = 1.0; +//CHECK: VarDecl {{.*}} f_val_3 'float' cinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00 +float f_val_4 = 1.0l; +//CHECK: VarDecl {{.*}} f_val_4 'float' cinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//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' +//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00 +double d_val_2 = 1.0bf16; +//CHECK: VarDecl {{.*}} d_val_2 'double' cinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 +double d_val_3 = 1.0f; +//CHECK: VarDecl {{.*}} d_val_3 'double' cinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' +//CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.000000e+00 +double d_val_4 = 1.0l; +//CHECK: VarDecl {{.*}} d_val_4 'double' cinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' +//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' +//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' +//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' +//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' +//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':'float' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'float' '+' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//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':'double' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'double' '+' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' +//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':'long double' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'long double' '+' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' +//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':'_Float16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +auto f16_uint = 1.0f16 + 1u; +//CHECK: VarDecl {{.*}} f16_uint '_Float16':'_Float16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned int' 1 +auto f16_long = 1.0f16 + 1l; +//CHECK: VarDecl {{.*}} f16_long '_Float16':'_Float16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'long' 1 +auto f16_ulong = 1.0f16 + 1ul; +//CHECK: VarDecl {{.*}} f16_ulong '_Float16':'_Float16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned long' 1 +auto f16_llong = 1.0f16 + 1ll; +//CHECK: VarDecl {{.*}} f16_llong '_Float16':'_Float16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'long long' 1 +auto f16_ullong = 1.0f16 + 1ull; +//CHECK: VarDecl {{.*}} f16_ullong '_Float16':'_Float16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned long long' 1 +auto f16_bool = 1.0f16 + true; +//CHECK: VarDecl {{.*}} f16_bool '_Float16':'_Float16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} '_Float16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} '_Float16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +//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(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':'float' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'float' '+' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//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':'double' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'double' '+' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' +//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':'long double' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'long double' '+' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' +//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':'BF16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'BF16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'BF16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +auto bf16_uint = 1.0bf16 + 1u; +//CHECK: VarDecl {{.*}} bf16_uint 'BF16':'BF16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'BF16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'BF16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned int' 1 +auto bf16_long = 1.0bf16 + 1l; +//CHECK: VarDecl {{.*}} bf16_long 'BF16':'BF16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'BF16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'BF16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'long' 1 +auto bf16_ulong = 1.0bf16 + 1ul; +//CHECK: VarDecl {{.*}} bf16_ulong 'BF16':'BF16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'BF16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'BF16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned long' 1 +auto bf16_llong = 1.0bf16 + 1ll; +//CHECK: VarDecl {{.*}} bf16_llong 'BF16':'BF16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'BF16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'BF16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'long long' 1 +auto bf16_ullong = 1.0bf16 + 1ull; +//CHECK: VarDecl {{.*}} bf16_ullong 'BF16':'BF16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'BF16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'BF16' +//CHECK-NEXT: IntegerLiteral {{.*}} 'unsigned long long' 1 +auto bf16_bool = 1.0bf16 + true; +//CHECK: VarDecl {{.*}} bf16_bool 'BF16':'BF16' cinit +//CHECK-NEXT: BinaryOperator {{.*}} 'BF16' '+' +//CHECK-NEXT: FloatingLiteral {{.*}} 'BF16' 1.000000e+00 +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'BF16' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +//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))' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (decltype(0.BF16))' lvalue Function {{.*}} 'f' 'int (decltype(0.BF16))' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'decltype(0.BF16)':'BF16' +//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)' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (_Float16)' lvalue Function {{.*}} 'f' 'int (_Float16)' +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//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)' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (float)' lvalue Function {{.*}} 'f' 'int (float)' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//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)' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (double)' lvalue Function {{.*}} 'f' 'int (double)' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' +//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)' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f' 'int (long double)' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' +//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)' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (int)' lvalue Function {{.*}} 'f' 'int (int)' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +//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)' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f_2' 'int (long double)' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//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)' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f_2' 'int (long double)' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' +//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)' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f_2' 'int (long double)' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'decltype(0.BF16)':'BF16' +//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)' +//CHECK-NEXT: DeclRefExpr {{.*}} 'int (long double)' lvalue Function {{.*}} 'f_2' 'int (long double)' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//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(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' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'decltype(0.BF16)':'BF16' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator BF16 {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S':'S' lvalue Var {{.*}} 'user_defined_val' 'S':'S' +_Float16 f16_val(user_defined_val); // calls operator _Float16() +//CHECK: VarDecl {{.*}} f16_val '_Float16' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} '_Float16 +//CHECK-NEXT: MemberExpr {{.*}} '' .operator _Float16 {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S':'S' lvalue Var {{.*}} 'user_defined_val' 'S':'S' +float f_val(user_defined_val); // calls operator float() +//CHECK: VarDecl {{.*}} f_val 'float' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'float' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator float {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S':'S' lvalue Var {{.*}} 'user_defined_val' 'S':'S' +double d_val(user_defined_val); // calls operator double() +//CHECK: VarDecl {{.*}} d_val 'double' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'double' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator double {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S':'S' lvalue Var {{.*}} 'user_defined_val' 'S':'S' +long double ld_val(user_defined_val); // calls operator long double() +//CHECK: VarDecl {{.*}} ld_val 'long double' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'long double' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator long double {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S':'S' lvalue Var {{.*}} 'user_defined_val' 'S':'S' +int i_val(user_defined_val); // calls operator int() +//CHECK: VarDecl {{.*}} i_val 'int' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'int' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator int {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S':'S' lvalue Var {{.*}} 'user_defined_val' 'S':'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' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'int' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator int {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S1' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S1':'S1' lvalue Var {{.*}} 'user_defined_val_2' 'S1':'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}}