diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -52,6 +52,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; @@ -1105,8 +1115,8 @@ CanQualType SatUnsignedShortFractTy, SatUnsignedFractTy, SatUnsignedLongFractTy; CanQualType HalfTy; // [OpenCL 6.1.1.1], ARM NEON - CanQualType BFloat16Ty; - CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3 + CanQualType BFloat16Ty; // [C++23 6.8.3p5][basic.extended.fp] + CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3 and [C++23 6.8.3p5][basic.extended.fp] CanQualType VoidPtrTy, NullPtrTy; CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy; CanQualType BuiltinFnTy; @@ -2803,14 +2813,38 @@ /// 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; + + /// C++23 6.8.2p12 [basic.fundamental] + /// Checks if extended floating point rules apply to a pair of types. + /// It returns true if both the types are C++23 floating point types and + /// at least one of them is a C++23 extended floating point type. It returns + /// false for pairs of standard C++23 floating point types. + bool doCXX23ExtendedFpTypesRulesApply(QualType T1, QualType T2) const; + + /// C++23 6.8.2p12 [basic.fundamental] + /// Returns true if \p Result is FRCR_Lesser or FRCR_Unordered rank. + bool + isCXX23SmallerOrUnorderedFloatingPointRank(FloatingRankCompareResult Result) const; + + /// C++23 6.8.2p12 [basic.fundamental] + /// Returns true if \p Result is FRCR_Equal, FRCR_Equal_Lesser_Subrank or + /// FRCR_Equal_Greater_Subrank. + bool isCXX23EqualFloatingPointRank(FloatingRankCompareResult Result) const; unsigned getTargetAddressSpace(LangAS AS) const; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2238,6 +2238,13 @@ bool isComplexType() const; // C99 6.2.5p11 (complex) bool isAnyComplexType() const; // C99 6.2.5p11 (complex) + Complex Int. bool isFloatingType() const; // C99 6.2.5p11 (real floating + complex) + /// C++23 6.8.2p12 [basic.fundamental] (standard floating point + extended + /// floating point) + bool isCXX23FloatingPointType(const ASTContext &Ctx) const; + /// C++23 6.8.2p12 [basic.fundamental] (standard floating point) + bool isCXX23StandardFloatingPointType(const ASTContext &Ctx) const; + /// C++23 6.8.2p12 [basic.fundamental] (extended floating point) + bool isCXX23ExtendedFloatingPointType(const ASTContext &Ctx) const; bool isHalfType() const; // OpenCL 6.1.1.1, NEON (IEEE 754-2008 half) bool isFloat16Type() const; // C11 extension ISO/IEC TS 18661 bool isBFloat16Type() const; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8928,6 +8928,7 @@ def err_nullptr_cast : Error< "cannot cast an object of type %select{'nullptr_t' to %1|%1 to 'nullptr_t'}0" >; +def err_cxx23_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< diff --git a/clang/include/clang/Lex/LiteralSupport.h b/clang/include/clang/Lex/LiteralSupport.h --- a/clang/include/clang/Lex/LiteralSupport.h +++ b/clang/include/clang/Lex/LiteralSupport.h @@ -81,6 +81,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 (C23) + bool isBFloat16 : 1; // 1.0bf uint8_t MicrosoftInteger; // Microsoft suffix extension i8, i16, i32, or i64. diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H +#include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" @@ -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 { diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -112,6 +112,85 @@ Ibm128Rank }; +constexpr unsigned CXX23FloatRankToIndex(clang::BuiltinType::Kind Kind) { + switch (Kind) { + case clang::BuiltinType::Float16: + return 0; + case clang::BuiltinType::BFloat16: + return 1; + case clang::BuiltinType::Float: + return 2; + case clang::BuiltinType::Double: + return 3; + case clang::BuiltinType::LongDouble: + return 4; + default: + // Both __float128 and __ibm128 are compiler extensions, not extended floating points. + // __float128 also predates the invention of floating-point types. + llvm_unreachable("Not a CXX23+ floating point builtin type"); + } +} + +// C++23 6.8.6p2 [conv.rank] +// Grid to determine the rank of a floating point type when compared with +// another floating point type. +constexpr std::array, 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 The locations that are relevant when searching for Doc comments /// related to \p D. static SmallVector @@ -7005,27 +7084,69 @@ } } +/// 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 (LHS->isCXX23FloatingPointType(*this) && + RHS->isCXX23FloatingPointType(*this)) { + 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); } +bool ASTContext::doCXX23ExtendedFpTypesRulesApply(QualType T1, + QualType T2) const { + return (((T1->isCXX23FloatingPointType(*this) && + T2->isCXX23FloatingPointType(*this))) && + (T1->isCXX23ExtendedFloatingPointType(*this) || + T2->isCXX23ExtendedFloatingPointType(*this))); +} + +bool ASTContext::isCXX23SmallerOrUnorderedFloatingPointRank( + FloatingRankCompareResult Result) const { + return (Result == FRCR_Lesser) || (Result == FRCR_Unordered); +} + +bool ASTContext::isCXX23EqualFloatingPointRank( + FloatingRankCompareResult Result) const { + return (Result == FRCR_Equal) || (Result == FRCR_Equal_Greater_Subrank) || + (Result == FRCR_Equal_Lesser_Subrank); +} + /// getIntegerRank - Return an integer conversion rank (C99 6.3.1.1p1). This /// routine will assert if passed a built-in type that isn't an integer or enum, /// or if it is not canonicalized. diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1358,6 +1358,7 @@ case BuiltinType::Float: OS << 'F'; break; case BuiltinType::LongDouble: OS << 'L'; break; case BuiltinType::Float128: OS << 'Q'; break; + case BuiltinType::BFloat16: OS << "BF16"; break; } } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2192,6 +2192,34 @@ return false; } +bool Type::isCXX23StandardFloatingPointType(const ASTContext &Ctx) const { + if (!Ctx.getLangOpts().CPlusPlus23) + return false; + 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(Ctx); + return false; +} + +bool Type::isCXX23ExtendedFloatingPointType(const ASTContext &Ctx) const { + if (!Ctx.getLangOpts().CPlusPlus23) + return false; + if (const auto *BT = dyn_cast(CanonicalType)) + return BT->getKind() == BuiltinType::Float16 || + (Ctx.getTargetInfo().hasFullBFloat16Type() && + (BT->getKind() == BuiltinType::BFloat16)); + if (const auto *CT = dyn_cast(CanonicalType)) + return CT->getElementType()->isCXX23ExtendedFloatingPointType(Ctx); + return false; +} + +bool Type::isCXX23FloatingPointType(const ASTContext &Ctx) const { + return isCXX23StandardFloatingPointType(Ctx) || + isCXX23ExtendedFloatingPointType(Ctx); +} + bool Type::hasFloatingRepresentation() const { if (const auto *VT = dyn_cast(CanonicalType)) return VT->getElementType()->isFloatingType(); diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -453,8 +453,12 @@ if (LangOpts.CPlusPlus26) // FIXME: Use correct value for C++26. Builder.defineMacro("__cplusplus", "202400L"); - else if (LangOpts.CPlusPlus23) + else if (LangOpts.CPlusPlus23) { Builder.defineMacro("__cplusplus", "202302L"); + // [C++23] 15.11p2 [cpp.predefined] + Builder.defineMacro("__STDCPP_FLOAT16_T__", "1"); + Builder.defineMacro("__STDCPP_BFLOAT16_T__", "1"); + } // [C++20] The integer literal 202002L. else if (LangOpts.CPlusPlus20) Builder.defineMacro("__cplusplus", "202002L"); diff --git a/clang/lib/Lex/LiteralSupport.cpp b/clang/lib/Lex/LiteralSupport.cpp --- a/clang/lib/Lex/LiteralSupport.cpp +++ b/clang/lib/Lex/LiteralSupport.cpp @@ -925,6 +925,7 @@ isAccum = false; hadError = false; isBitInt = false; + isBFloat16 = false; // This routine assumes that the range begin/end matches the regex for integer // and FP constants (specifically, the 'pp-number' regex), and assumes that @@ -1025,6 +1026,21 @@ isFloat = true; continue; // Success. + // C++23 5.13.4 [lex.fcon] + case 'b': + case 'B': + if (!isFPConstant) + break; // Error for integer constant. + if (s + 3 < ThisTokEnd && (s[1] == 'f' || s[1] == 'F') && s[2] == '1' && + s[3] == '6') { + if (HasSize) + break; + HasSize = true; + s += 3; + isBFloat16 = true; + continue; + } + break; case 'q': // FP Suffix for "__float128" case 'Q': if (!isFPConstant) break; // Error for integer constant. @@ -1157,6 +1173,7 @@ saw_fixed_point_suffix = false; isFract = false; isAccum = false; + isBFloat16 = false; } saw_ud_suffix = true; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -699,6 +699,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 (Context.doCXX23ExtendedFpTypesRulesApply(ExprTy, TypeTy) && + Kind == CK_FloatingCast && E->isPRValue() && + (CCK == CCK_ImplicitConversion)) { + if (Context.isCXX23SmallerOrUnorderedFloatingPointRank( + Context.getFloatingTypeOrder(TypeTy, ExprTy))) { + Diag(E->getExprLoc(), + diag::err_cxx23_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); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1386,6 +1386,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() && + Self.Context.doCXX23ExtendedFpTypesRulesApply(DestType, SrcType)) { + // FIXME: Support for cast between fp16 and bf16 doesn't exist yet. + if (!((DestType->isBFloat16Type() || DestType->isFloat16Type()) && + (SrcType->isBFloat16Type() || SrcType->isFloat16Type()))) { + Kind = CK_FloatingCast; + return TC_Success; + } + } + // Reverse integral promotion/conversion. All such conversions are themselves // again integral promotions or conversions and are thus already handled by // p2 (TryDirectInitialization above). diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -11746,7 +11746,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 @@ -15056,9 +15056,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); @@ -15484,7 +15487,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; @@ -15502,12 +15505,14 @@ DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_float_precision); } // ... or possibly if we're increasing rank, too - else if (Order < 0) { + else if (Order == FRCR_Lesser) { if (S.SourceMgr.isInSystemMacro(CC)) return; DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_double_promotion); } + assert(Order != FRCR_Unordered && + "Unordered floating types are not allowed."); return; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -805,7 +805,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, @@ -814,7 +814,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( @@ -1165,14 +1166,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 @@ -1225,16 +1228,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; @@ -4097,6 +4118,8 @@ Ty = Context.Float16Ty; else if (Literal.isFloat128) Ty = Context.Float128Ty; + else if (Literal.isBFloat16) + Ty = Context.BFloat16Ty; else Ty = Context.DoubleTy; @@ -10849,7 +10872,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; } @@ -11037,8 +11061,18 @@ // 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.doCXX23ExtendedFpTypesRulesApply(ScalarTy, VectorEltTy) && + S.Context.isCXX23SmallerOrUnorderedFloatingPointRank(Order)) + return true; + + if (!CstScalar && (Order == FRCR_Lesser)) return true; // If the scalar cannot be safely casted to the vector element type, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -400,7 +400,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); @@ -2119,6 +2119,16 @@ (FromType->isFloat16Type() || FromType->isHalfType()))) return false; + // C++23 7.3.10p1 [conv.double] + // A prvalue of floating-point type can be converted to a prvalue of another + // floating-point type with a greater or equal conversion rank + // ([conv.rank]). A prvalue of standard floating-point type can be converted + // to a prvalue of another standard floating-point type. + if (S.Context.doCXX23ExtendedFpTypesRulesApply(FromType, ToType) && + S.Context.isCXX23SmallerOrUnorderedFloatingPointRank( + S.Context.getFloatingTypeOrder(ToType, FromType))) + return false; + // Conversions between IEEE-quad and IBM-extended semantics are not // permitted. const llvm::fltSemantics &FromSem = @@ -4292,6 +4302,73 @@ ? ImplicitConversionSequence::Better : ImplicitConversionSequence::Worse; + // C++23 12.2.4.3p4: + // A conversion in either direction between floating-point type FP1 and + // floating-point type FP2 is better than a conversion in the same direction + // between FP1 and arithmetic type T3 if: + // 1) The floating-point conversion rank ([conv.rank]) of FP1 is equal to the + // rank of FP2, and 2) T3 is not a floating-point type, or T3 is a + // floating-point type whose rank is not equal to the rank of FP1, or the + // floating-point conversion subrank ([conv.rank]) of FP2 is greater than the + // subrank of T3. + if (S.Context.getLangOpts().CPlusPlus23) { + if (SCS1.getFromType()->isCXX23FloatingPointType(S.Context) && + S.Context.hasSameType(SCS1.getFromType(), SCS2.getFromType())) { + + if ((SCS1.Second == ICK_Floating_Conversion) && + SCS1.getToType(1)->isCXX23FloatingPointType(S.Context) && + S.Context.isCXX23EqualFloatingPointRank( + S.Context.getFloatingTypeOrder(SCS1.getToType(1), + SCS1.getFromType()))) { + + if (SCS2.getToType(1)->isArithmeticType()) { + if (!SCS2.getToType(1)->isCXX23FloatingPointType(S.Context)) { + return ImplicitConversionSequence::Better; + } + + if (!S.Context.isCXX23EqualFloatingPointRank( + S.Context.getFloatingTypeOrder(SCS2.getToType(1), + SCS1.getFromType()))) { + return ImplicitConversionSequence::Better; + } + + if (S.Context.getFloatingTypeOrder(SCS1.getToType(1), + SCS2.getToType(1)) == + FRCR_Equal_Greater_Subrank) { + return ImplicitConversionSequence::Better; + } + return ImplicitConversionSequence::Worse; + } + } + + if ((SCS2.Second == ICK_Floating_Conversion) && + SCS2.getToType(1)->isCXX23FloatingPointType(S.Context) && + S.Context.isCXX23EqualFloatingPointRank( + S.Context.getFloatingTypeOrder(SCS2.getToType(1), + SCS2.getFromType()))) { + if (SCS1.getToType(1)->isArithmeticType()) { + if (!SCS1.getToType(1)->isCXX23FloatingPointType(S.Context)) { + return ImplicitConversionSequence::Worse; + } + + if (!S.Context.isCXX23EqualFloatingPointRank( + S.Context.getFloatingTypeOrder(SCS1.getToType(1), + SCS2.getFromType()))) { + return ImplicitConversionSequence::Worse; + } + + if (S.Context.getFloatingTypeOrder(SCS2.getToType(1), + SCS1.getToType(1)) == + FRCR_Equal_Greater_Subrank) { + return ImplicitConversionSequence::Worse; + } + + assert(false && "Should not reach here"); + } + } + } + } + // C++ [over.ics.rank]p4b2: // // If class B is derived directly or indirectly from class A, @@ -8536,7 +8613,7 @@ // Flag if we encounter an arithmetic type. HasArithmeticOrEnumeralTypes = - HasArithmeticOrEnumeralTypes || Ty->isArithmeticType(); + HasArithmeticOrEnumeralTypes || Ty->isArithmeticType(); if (Ty->isObjCIdType() || Ty->isObjCClassType()) PointerTypes.insert(Ty); diff --git a/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp b/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx23-fp-ext-std-names-p1467r9.cpp @@ -0,0 +1,499 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature +// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-linux-gnu -target-feature +fullbf16 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +// CHECK-LABEL: define {{[^@]+}}@_Z1fDF16b +// CHECK-SAME: (bfloat noundef [[V:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: [[V_ADDR:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: store bfloat [[V]], ptr [[V_ADDR]], align 2 +// CHECK-NEXT: ret i32 1 +// +int f(decltype(0.BF16) v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1fDF16_ +// CHECK-SAME: (half noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca half, align 2 +// CHECK-NEXT: store half [[V]], ptr [[V_ADDR]], align 2 +// CHECK-NEXT: ret i32 1 +// +int f(_Float16 v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1ff +// CHECK-SAME: (float noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca float, align 4 +// CHECK-NEXT: store float [[V]], ptr [[V_ADDR]], align 4 +// CHECK-NEXT: ret i32 1 +// +int f(float v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1fd +// CHECK-SAME: (double noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca double, align 8 +// CHECK-NEXT: store double [[V]], ptr [[V_ADDR]], align 8 +// CHECK-NEXT: ret i32 1 +// +int f(double v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1fe +// CHECK-SAME: (x86_fp80 noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca x86_fp80, align 16 +// CHECK-NEXT: store x86_fp80 [[V]], ptr [[V_ADDR]], align 16 +// CHECK-NEXT: ret i32 1 +// +int f(long double v) { return 1; } +// CHECK-LABEL: define {{[^@]+}}@_Z1fi +// CHECK-SAME: (i32 noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[V]], ptr [[V_ADDR]], align 4 +// CHECK-NEXT: ret i32 1 +// +int f(int v) { return 1; } + +// CHECK-LABEL: define {{[^@]+}}@_Z3f_2e +// CHECK-SAME: (x86_fp80 noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK: [[V_ADDR:%.*]] = alloca x86_fp80, align 16 +// CHECK-NEXT: store x86_fp80 [[V]], ptr [[V_ADDR]], align 16 +// CHECK-NEXT: ret i32 1 +// +int f_2(long double v) { return 1; } + +struct S { + + operator decltype(0.BF16)() const { + return 0.0bf16; + } + + operator _Float16() const { + return 0.0f16; + } + + operator float() const { + return 0.0f; + } + + operator double() const { + return 0.0; + } + + operator long double() const { + return 0.0L; + } + + operator int() const { + return 0; + } +}; + +struct S1 { + operator _Float16() const { + return 0.0f16; + } + operator float() const { + return 0.0f; + } + operator double() const { + return 0.0; + } + operator long double() const { + return 0.0L; + } + + operator int() const { + return 0; + } +}; + +// CHECK-LABEL: define {{[^@]+}}@_Z5test1v +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: [[F16_VAL_6:%.*]] = alloca half, align 2 +// CHECK-NEXT: [[F16_VAL_8:%.*]] = alloca half, align 2 +// CHECK-NEXT: [[F16_VAL_9:%.*]] = alloca half, align 2 +// CHECK-NEXT: [[F16_VAL_10:%.*]] = alloca half, align 2 +// CHECK-NEXT: [[F16_VAL_11:%.*]] = alloca half, align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_6]], align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_8]], align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_9]], align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_10]], align 2 +// CHECK-NEXT: store half 0xH3C00, ptr [[F16_VAL_11]], align 2 +// CHECK-NEXT: ret void +// +void test1() { + _Float16 f16_val_6 = 1.0f16; + _Float16 f16_val_8 = static_cast<_Float16>(1.0f); + _Float16 f16_val_9 = static_cast<_Float16>(1.0); + _Float16 f16_val_10 = static_cast<_Float16>(1.0l); + _Float16 f16_val_11 = static_cast<_Float16>(1.0f16); +} + +// CHECK-LABEL: define {{[^@]+}}@_Z5test2v +// CHECK-SAME: () #[[ATTR0]] { +// CHECK: [[BF16_VAL_5:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: [[BF16_VAL_7:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: [[BF16_VAL_8:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: [[BF16_VAL_9:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: [[BF16_VAL_10:%.*]] = alloca bfloat, align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_5]], align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_7]], align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_8]], align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_9]], align 2 +// CHECK-NEXT: store bfloat 0xR3F80, ptr [[BF16_VAL_10]], align 2 +// CHECK-NEXT: ret void +// +void test2() { + decltype(0.BF16) bf16_val_5 = 1.0bf16; + decltype(0.BF16) bf16_val_7 = static_cast(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() +} diff --git a/clang/test/CodeGenCXX/cxx23-vector-bfloat16.cpp b/clang/test/CodeGenCXX/cxx23-vector-bfloat16.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx23-vector-bfloat16.cpp @@ -0,0 +1,67 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2 +// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-linux-gnu -target-feature +fullbf16 -S -emit-llvm %s -o - | FileCheck %s + +typedef decltype(0.0BF16) v8bfloat16 __attribute__((__vector_size__(16))); + +// CHECK-LABEL: define dso_local void @_Z11test_vectorDv8_DF16bS_ +// CHECK-SAME: (<8 x bfloat> noundef [[A:%.*]], <8 x bfloat> noundef [[B:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: [[A_ADDR:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: [[C:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: store <8 x bfloat> [[A]], ptr [[A_ADDR]], align 16 +// CHECK-NEXT: store <8 x bfloat> [[B]], ptr [[B_ADDR]], align 16 +// CHECK-NEXT: [[TMP0:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16 +// CHECK-NEXT: [[TMP1:%.*]] = load <8 x bfloat>, ptr [[B_ADDR]], align 16 +// CHECK-NEXT: [[ADD:%.*]] = fadd <8 x bfloat> [[TMP0]], [[TMP1]] +// CHECK-NEXT: store <8 x bfloat> [[ADD]], ptr [[C]], align 16 +// CHECK-NEXT: [[TMP2:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16 +// CHECK-NEXT: [[TMP3:%.*]] = load <8 x bfloat>, ptr [[B_ADDR]], align 16 +// CHECK-NEXT: [[SUB:%.*]] = fsub <8 x bfloat> [[TMP2]], [[TMP3]] +// CHECK-NEXT: store <8 x bfloat> [[SUB]], ptr [[C]], align 16 +// CHECK-NEXT: [[TMP4:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16 +// CHECK-NEXT: [[TMP5:%.*]] = load <8 x bfloat>, ptr [[B_ADDR]], align 16 +// CHECK-NEXT: [[MUL:%.*]] = fmul <8 x bfloat> [[TMP4]], [[TMP5]] +// CHECK-NEXT: store <8 x bfloat> [[MUL]], ptr [[C]], align 16 +// CHECK-NEXT: [[TMP6:%.*]] = load <8 x bfloat>, ptr [[A_ADDR]], align 16 +// CHECK-NEXT: [[TMP7:%.*]] = load <8 x bfloat>, ptr [[B_ADDR]], align 16 +// CHECK-NEXT: [[DIV:%.*]] = fdiv <8 x bfloat> [[TMP6]], [[TMP7]] +// CHECK-NEXT: store <8 x bfloat> [[DIV]], ptr [[C]], align 16 +// CHECK-NEXT: ret void +// +void test_vector(v8bfloat16 a, v8bfloat16 b) { + v8bfloat16 c; + c = a + b; + c = a - b; + c = a * b; + c = a / b; +} + +// CHECK-LABEL: define dso_local void @_Z13test_vector_2v +// CHECK-SAME: () #[[ATTR1:[0-9]+]] { +// CHECK: [[A:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: [[B:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL1:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: [[C:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: [[D:%.*]] = alloca <8 x bfloat>, align 16 +// CHECK-NEXT: store <8 x bfloat> zeroinitializer, ptr [[DOTCOMPOUNDLITERAL]], align 16 +// CHECK-NEXT: [[TMP0:%.*]] = load <8 x bfloat>, ptr [[DOTCOMPOUNDLITERAL]], align 16 +// CHECK-NEXT: store <8 x bfloat> [[TMP0]], ptr [[A]], align 16 +// CHECK-NEXT: store <8 x bfloat> , ptr [[DOTCOMPOUNDLITERAL1]], align 16 +// CHECK-NEXT: [[TMP1:%.*]] = load <8 x bfloat>, ptr [[DOTCOMPOUNDLITERAL1]], align 16 +// CHECK-NEXT: store <8 x bfloat> [[TMP1]], ptr [[B]], align 16 +// CHECK-NEXT: [[TMP2:%.*]] = load <8 x bfloat>, ptr [[A]], align 16 +// CHECK-NEXT: [[ELT_ABS:%.*]] = call <8 x bfloat> @llvm.fabs.v8bf16(<8 x bfloat> [[TMP2]]) +// CHECK-NEXT: store <8 x bfloat> [[ELT_ABS]], ptr [[C]], align 16 +// CHECK-NEXT: [[TMP3:%.*]] = load <8 x bfloat>, ptr [[C]], align 16 +// CHECK-NEXT: [[ELT_ABS2:%.*]] = call <8 x bfloat> @llvm.fabs.v8bf16(<8 x bfloat> [[TMP3]]) +// CHECK-NEXT: store <8 x bfloat> [[ELT_ABS2]], ptr [[D]], align 16 +// CHECK-NEXT: ret void +// +void test_vector_2() { + v8bfloat16 a = (v8bfloat16){0.0bf16}; + v8bfloat16 b = (v8bfloat16){1.0bf16}; + v8bfloat16 c = __builtin_elementwise_abs(a); + v8bfloat16 d = __builtin_elementwise_abs(c); +} + diff --git a/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp b/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/cxx23-fp-ext-std-names-p1467r9.cpp @@ -0,0 +1,465 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++23 -target-feature +fullbf16 -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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' 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' lvalue Var {{.*}} 'user_defined_val' 'S' +_Float16 f16_val(user_defined_val); // calls operator _Float16() +//CHECK: VarDecl {{.*}} f16_val '_Float16' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} '_Float16' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} '_Float16 +//CHECK-NEXT: MemberExpr {{.*}} '' .operator _Float16 {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S' +float f_val(user_defined_val); // calls operator float() +//CHECK: VarDecl {{.*}} f_val 'float' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'float' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator float {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S' +double d_val(user_defined_val); // calls operator double() +//CHECK: VarDecl {{.*}} d_val 'double' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'double' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator double {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S' +long double ld_val(user_defined_val); // calls operator long double() +//CHECK: VarDecl {{.*}} ld_val 'long double' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'long double' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator long double {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S' +int i_val(user_defined_val); // calls operator int() +//CHECK: VarDecl {{.*}} i_val 'int' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'int' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator int {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 'user_defined_val' 'S' +struct S1 { + operator _Float16() const { + return 0.0f16; + } + operator float() const { + return 0.0f; + } + operator double() const { + return 0.0; + } + operator long double() const { + return 0.0L; + } + operator int() const { + return 0; + } +}; + +S1 user_defined_val_2; +// User-defined overload cases +decltype(0.0BF16) bfloat16_val_2(user_defined_val_2); // calls operator int() +//CHECK: VarDecl {{.*}} bfloat16_val_2 'decltype(0.BF16)':'__bf16' callinit +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'decltype(0.BF16)':'__bf16' +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' +//CHECK-NEXT: CXXMemberCallExpr {{.*}} 'int' +//CHECK-NEXT: MemberExpr {{.*}} '' .operator int {{.*}} +//CHECK-NEXT: ImplicitCastExpr {{.*}} 'const S1' lvalue +//CHECK-NEXT: DeclRefExpr {{.*}} 'S1' lvalue Var {{.*}} 'user_defined_val_2' 'S1' +struct S2 { + operator decltype(0.0BF16)() const { // expected-note {{candidate function}} + return 0.0bf16; + } + + operator _Float16() const { // expected-note {{candidate function}} + return 0.0f16; + } + operator double() const { // expected-note {{candidate function}} + return 0.0; + } + operator long double() const { // expected-note {{candidate function}} + return 0.0L; + } + operator int() const { // expected-note {{candidate function}} + return 0; + } +}; + +S2 user_defined_val_3; +// User-defined overload cases +float float_val_2(user_defined_val_3); // expected-error {{conversion from 'S2' to 'float' is ambiguous}}