Author: Timm Baeder Date: 2023-09-30T20:08:22+02:00 New Revision: 16b9e6fbac4c0bd94c66e7670a41b5c266cf7bff
URL: https://github.com/llvm/llvm-project/commit/16b9e6fbac4c0bd94c66e7670a41b5c266cf7bff DIFF: https://github.com/llvm/llvm-project/commit/16b9e6fbac4c0bd94c66e7670a41b5c266cf7bff.diff LOG: [clang][Interp] Add IntegralAP for arbitrary-precision integers (#65844) This adds `IntegralAP` backing the two new primtypes `IntAP` (unsigned arbitrary-precision int) and `IntAPS` (same but signed). We use this for `int128` support (which isn't available on all host systems we support AFAIK) and I think we can also use this for `_BitInt` later. Added: clang/lib/AST/Interp/IntegralAP.h Modified: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Context.cpp clang/lib/AST/Interp/Descriptor.cpp clang/lib/AST/Interp/EvalEmitter.cpp clang/lib/AST/Interp/Integral.h clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/InterpStack.h clang/lib/AST/Interp/Opcodes.td clang/lib/AST/Interp/PrimType.cpp clang/lib/AST/Interp/PrimType.h clang/test/AST/Interp/literals.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 46906377863bd74..4b251931aa01d8a 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -171,14 +171,17 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) { return this->discard(SubExpr); std::optional<PrimType> FromT = classify(SubExpr->getType()); std::optional<PrimType> ToT = classify(CE->getType()); + if (!FromT || !ToT) return false; if (!this->visit(SubExpr)) return false; - if (FromT == ToT) + if (FromT == ToT) { + assert(ToT != PT_IntAP && ToT != PT_IntAPS); return true; + } return this->emitCast(*FromT, *ToT, CE); } @@ -1638,6 +1641,9 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(QualType QT, return this->emitZeroSint64(E); case PT_Uint64: return this->emitZeroUint64(E); + case PT_IntAP: + case PT_IntAPS: + assert(false); case PT_Ptr: return this->emitNullPtr(E); case PT_FnPtr: @@ -1877,6 +1883,9 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) { return this->emitConstSint64(Value, E); case PT_Uint64: return this->emitConstUint64(Value, E); + case PT_IntAP: + case PT_IntAPS: + assert(false); case PT_Bool: return this->emitConstBool(Value, E); case PT_Ptr: diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp index e84c0f6aae7ee93..04710cd0f2c28ee 100644 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -103,7 +103,7 @@ std::optional<PrimType> Context::classify(QualType T) const { case 8: return PT_Sint8; default: - return std::nullopt; + return PT_IntAPS; } } @@ -118,7 +118,7 @@ std::optional<PrimType> Context::classify(QualType T) const { case 8: return PT_Uint8; default: - return std::nullopt; + return PT_IntAP; } } diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp index db49a569eff33ea..4ecb7466998e705 100644 --- a/clang/lib/AST/Interp/Descriptor.cpp +++ b/clang/lib/AST/Interp/Descriptor.cpp @@ -10,6 +10,7 @@ #include "Boolean.h" #include "Floating.h" #include "FunctionPointer.h" +#include "IntegralAP.h" #include "Pointer.h" #include "PrimType.h" #include "Record.h" @@ -182,6 +183,10 @@ static BlockCtorFn getCtorPrim(PrimType Type) { // constructor called. if (Type == PT_Float) return ctorTy<PrimConv<PT_Float>::T>; + if (Type == PT_IntAP) + return ctorTy<PrimConv<PT_IntAP>::T>; + if (Type == PT_IntAPS) + return ctorTy<PrimConv<PT_IntAPS>::T>; COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr); } @@ -191,6 +196,10 @@ static BlockDtorFn getDtorPrim(PrimType Type) { // destructor called, since they might allocate memory. if (Type == PT_Float) return dtorTy<PrimConv<PT_Float>::T>; + if (Type == PT_IntAP) + return dtorTy<PrimConv<PT_IntAP>::T>; + if (Type == PT_IntAPS) + return dtorTy<PrimConv<PT_IntAPS>::T>; COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr); } diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp index bdf800c60f1723f..f46ef1067cf52a0 100644 --- a/clang/lib/AST/Interp/EvalEmitter.cpp +++ b/clang/lib/AST/Interp/EvalEmitter.cpp @@ -9,6 +9,7 @@ #include "EvalEmitter.h" #include "ByteCodeGenError.h" #include "Context.h" +#include "IntegralAP.h" #include "Interp.h" #include "Opcode.h" #include "clang/AST/DeclCXX.h" diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h index 0295a9c3b5c898c..4dbe9c9bcb14b43 100644 --- a/clang/lib/AST/Interp/Integral.h +++ b/clang/lib/AST/Interp/Integral.h @@ -29,6 +29,8 @@ namespace interp { using APInt = llvm::APInt; using APSInt = llvm::APSInt; +template <bool Signed> class IntegralAP; + // Helper structure to select the representation. template <unsigned Bits, bool Signed> struct Repr; template <> struct Repr<8, false> { using Type = uint8_t; }; @@ -61,6 +63,8 @@ template <unsigned Bits, bool Signed> class Integral final { template <typename T> explicit Integral(T V) : V(V) {} public: + using AsUnsigned = Integral<Bits, false>; + /// Zero-initializes an integral. Integral() : V(0) {} diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h new file mode 100644 index 000000000000000..a3a8dfda020960a --- /dev/null +++ b/clang/lib/AST/Interp/IntegralAP.h @@ -0,0 +1,256 @@ +//===--- Integral.h - Wrapper for numeric types for the VM ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the VM types and helpers operating on types. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_INTEGRAL_AP_H +#define LLVM_CLANG_AST_INTERP_INTEGRAL_AP_H + +#include "clang/AST/APValue.h" +#include "clang/AST/ComparisonCategories.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include <cstddef> +#include <cstdint> + +#include "Primitives.h" + +namespace clang { +namespace interp { + +using APInt = llvm::APInt; +using APSInt = llvm::APSInt; +template <unsigned Bits, bool Signed> class Integral; +class Boolean; + +template <bool Signed> class IntegralAP final { +public: + APSInt V; + +public: + using AsUnsigned = IntegralAP<false>; + + template <typename T> + IntegralAP(T Value) : V(APInt(sizeof(T) * 8, Value, std::is_signed_v<T>)) {} + + IntegralAP(APInt V) : V(V) {} + IntegralAP(APSInt V) : V(V) {} + /// Arbitrary value for initialized variables. + IntegralAP() : V(APSInt::getMaxValue(1024, Signed)) {} + + IntegralAP operator-() const { return IntegralAP(-V); } + bool operator>(IntegralAP RHS) const { return V > RHS.V; } + bool operator>=(IntegralAP RHS) const { return V >= RHS.V; } + bool operator<(IntegralAP RHS) const { return V < RHS.V; } + bool operator<=(IntegralAP RHS) const { return V <= RHS.V; } + + explicit operator bool() const { return !V.isZero(); } + explicit operator int8_t() const { return V.getSExtValue(); } + explicit operator uint8_t() const { return V.getZExtValue(); } + explicit operator int16_t() const { return V.getSExtValue(); } + explicit operator uint16_t() const { return V.getZExtValue(); } + explicit operator int32_t() const { return V.getSExtValue(); } + explicit operator uint32_t() const { return V.getZExtValue(); } + explicit operator int64_t() const { return V.getSExtValue(); } + explicit operator uint64_t() const { return V.getZExtValue(); } + + template <typename T> static IntegralAP from(T Value, unsigned NumBits = 0) { + assert(NumBits > 0); + APSInt Copy = APSInt(APInt(NumBits, Value, Signed), !Signed); + + return IntegralAP<Signed>(Copy); + } + + template <bool InputSigned> + static IntegralAP from(IntegralAP<InputSigned> V, unsigned NumBits = 0) { + if constexpr (Signed == InputSigned) + return V; + + APSInt Copy = V.V; + Copy.setIsSigned(Signed); + + return IntegralAP<Signed>(Copy); + } + + template <unsigned Bits, bool InputSigned> + static IntegralAP from(Integral<Bits, InputSigned> I) { + // FIXME: Take bits parameter. + APSInt Copy = + APSInt(APInt(128, static_cast<int64_t>(I), InputSigned), !Signed); + Copy.setIsSigned(Signed); + + assert(Copy.isSigned() == Signed); + return IntegralAP<Signed>(Copy); + } + static IntegralAP from(const Boolean &B) { + assert(false); + return IntegralAP::zero(); + } + + static IntegralAP zero() { + assert(false); + return IntegralAP(0); + } + + // FIXME: This can't be static if the bitwidth depends on V. + static constexpr unsigned bitWidth() { return 128; } + + APSInt toAPSInt(unsigned Bits = 0) const { return V; } + APValue toAPValue() const { return APValue(V); } + + bool isZero() const { return V.isZero(); } + bool isPositive() const { return V.isNonNegative(); } + bool isNegative() const { return !V.isNonNegative(); } + bool isMin() const { return V.isMinValue(); } + bool isMax() const { return V.isMaxValue(); } + static bool isSigned() { return Signed; } + bool isMinusOne() const { return Signed && V == -1; } + + unsigned countLeadingZeros() const { return V.countl_zero(); } + + void print(llvm::raw_ostream &OS) const { OS << V; } + + IntegralAP truncate(unsigned bitWidth) const { + assert(false); + return V; + } + + IntegralAP<false> toUnsigned() const { + APSInt Copy = V; + Copy.setIsSigned(false); + return IntegralAP<false>(Copy); + } + + ComparisonCategoryResult compare(const IntegralAP &RHS) const { + return Compare(V, RHS.V); + } + + static bool increment(IntegralAP A, IntegralAP *R) { + assert(false); + *R = IntegralAP(A.V + 1); + return false; + } + + static bool decrement(IntegralAP A, IntegralAP *R) { + assert(false); + *R = IntegralAP(A.V - 1); + return false; + } + + static bool add(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { + return CheckAddUB(A, B, OpBits, R); + } + + static bool sub(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { + /// FIXME: Gotta check if the result fits into OpBits bits. + return CheckSubUB(A, B, R); + } + + static bool mul(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { + assert(false); + // return CheckMulUB(A.V, B.V, R->V); + return false; + } + + static bool rem(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { + assert(false); + *R = IntegralAP(A.V % B.V); + return false; + } + + static bool div(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { + assert(false); + *R = IntegralAP(A.V / B.V); + return false; + } + + static bool bitAnd(IntegralAP A, IntegralAP B, unsigned OpBits, + IntegralAP *R) { + assert(false); + *R = IntegralAP(A.V & B.V); + return false; + } + + static bool bitOr(IntegralAP A, IntegralAP B, unsigned OpBits, + IntegralAP *R) { + assert(false); + *R = IntegralAP(A.V | B.V); + return false; + } + + static bool bitXor(IntegralAP A, IntegralAP B, unsigned OpBits, + IntegralAP *R) { + assert(false); + *R = IntegralAP(A.V ^ B.V); + return false; + } + + static bool neg(const IntegralAP &A, IntegralAP *R) { + APSInt AI = A.V; + + AI.setIsSigned(Signed); + *R = IntegralAP(AI); + return false; + } + + static bool comp(IntegralAP A, IntegralAP *R) { + assert(false); + *R = IntegralAP(~A.V); + return false; + } + + static void shiftLeft(const IntegralAP A, const IntegralAP B, unsigned OpBits, + IntegralAP *R) { + *R = IntegralAP(A.V << B.V.getZExtValue()); + } + + static void shiftRight(const IntegralAP A, const IntegralAP B, + unsigned OpBits, IntegralAP *R) { + *R = IntegralAP(A.V >> B.V.getZExtValue()); + } + +private: + static bool CheckAddUB(const IntegralAP &A, const IntegralAP &B, + unsigned BitWidth, IntegralAP *R) { + if (!A.isSigned()) { + R->V = A.V + B.V; + return false; + } + + const APSInt &LHS = A.V; + const APSInt &RHS = B.V; + + APSInt Value(LHS.extend(BitWidth) + RHS.extend(BitWidth), false); + APSInt Result = Value.trunc(LHS.getBitWidth()); + if (Result.extend(BitWidth) != Value) + return true; + + R->V = Result; + return false; + } + static bool CheckSubUB(const IntegralAP &A, const IntegralAP &B, + IntegralAP *R) { + R->V = A.V - B.V; + return false; // Success! + } +}; + +template <bool Signed> +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + IntegralAP<Signed> I) { + I.print(OS); + return OS; +} + +} // namespace interp +} // namespace clang + +#endif diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index dd37150b63f6db0..9d5ec3315415cf7 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -294,7 +294,11 @@ bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS, return true; } else { S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type; - return S.noteUndefinedBehavior(); + if (!S.noteUndefinedBehavior()) { + S.Stk.pop<T>(); + return false; + } + return true; } } @@ -1579,7 +1583,7 @@ bool CastFloatingIntegral(InterpState &S, CodePtr OpPC) { S.Stk.push<T>(T(F.isNonZero())); return true; } else { - APSInt Result(std::max(8u, T::bitWidth() + 1), + APSInt Result(std::max(8u, T::bitWidth()), /*IsUnsigned=*/!T::isSigned()); auto Status = F.convertToInteger(Result); @@ -1665,9 +1669,11 @@ inline bool Shr(InterpState &S, CodePtr OpPC) { if (!CheckShift(S, OpPC, LHS, RHS, Bits)) return false; - Integral<LT::bitWidth(), false> R; - Integral<LT::bitWidth(), false>::shiftRight(LHS.toUnsigned(), RHS, Bits, &R); - S.Stk.push<LT>(R); + typename LT::AsUnsigned R; + LT::AsUnsigned::shiftRight(LT::AsUnsigned::from(LHS), + LT::AsUnsigned::from(RHS), Bits, &R); + S.Stk.push<LT>(LT::from(R)); + return true; } @@ -1682,9 +1688,10 @@ inline bool Shl(InterpState &S, CodePtr OpPC) { if (!CheckShift(S, OpPC, LHS, RHS, Bits)) return false; - Integral<LT::bitWidth(), false> R; - Integral<LT::bitWidth(), false>::shiftLeft(LHS.toUnsigned(), RHS, Bits, &R); - S.Stk.push<LT>(R); + typename LT::AsUnsigned R; + LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS), + LT::AsUnsigned::from(RHS), Bits, &R); + S.Stk.push<LT>(LT::from(R)); return true; } diff --git a/clang/lib/AST/Interp/InterpStack.h b/clang/lib/AST/Interp/InterpStack.h index ab4351a6dc679fd..3fd0f63c781fc70 100644 --- a/clang/lib/AST/Interp/InterpStack.h +++ b/clang/lib/AST/Interp/InterpStack.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_AST_INTERP_INTERPSTACK_H #include "FunctionPointer.h" +#include "IntegralAP.h" #include "PrimType.h" #include <memory> #include <vector> @@ -183,6 +184,10 @@ class InterpStack final { return PT_Float; else if constexpr (std::is_same_v<T, FunctionPointer>) return PT_FnPtr; + else if constexpr (std::is_same_v<T, IntegralAP<true>>) + return PT_IntAP; + else if constexpr (std::is_same_v<T, IntegralAP<false>>) + return PT_IntAP; llvm_unreachable("unknown type push()'ed into InterpStack"); } diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index 0ce64b769b01fb7..9fc4938bb37bde8 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -25,6 +25,8 @@ def Sint32 : Type; def Uint32 : Type; def Sint64 : Type; def Uint64 : Type; +def IntAP : Type; +def IntAPS : Type; def Float : Type; def Ptr : Type; def FnPtr : Type; @@ -67,7 +69,7 @@ class TypeClass { def IntegerTypeClass : TypeClass { let Types = [Sint8, Uint8, Sint16, Uint16, Sint32, - Uint32, Sint64, Uint64]; + Uint32, Sint64, Uint64, IntAP, IntAPS]; } def NumberTypeClass : TypeClass { @@ -554,11 +556,11 @@ def Comp: Opcode { //===----------------------------------------------------------------------===// def FromCastTypeClass : TypeClass { - let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool, IntAP, IntAPS]; } def ToCastTypeClass : TypeClass { - let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool, IntAP, IntAPS]; } def Cast: Opcode { diff --git a/clang/lib/AST/Interp/PrimType.cpp b/clang/lib/AST/Interp/PrimType.cpp index a9b5d8ea8cc8c7c..9b96dcfe6a272fc 100644 --- a/clang/lib/AST/Interp/PrimType.cpp +++ b/clang/lib/AST/Interp/PrimType.cpp @@ -10,6 +10,7 @@ #include "Boolean.h" #include "Floating.h" #include "FunctionPointer.h" +#include "IntegralAP.h" #include "Pointer.h" using namespace clang; diff --git a/clang/lib/AST/Interp/PrimType.h b/clang/lib/AST/Interp/PrimType.h index 7c7ee6120b89a4a..8c5e87f37be1867 100644 --- a/clang/lib/AST/Interp/PrimType.h +++ b/clang/lib/AST/Interp/PrimType.h @@ -25,6 +25,7 @@ class Pointer; class Boolean; class Floating; class FunctionPointer; +template <bool Signed> class IntegralAP; template <unsigned Bits, bool Signed> class Integral; /// Enumeration of the primitive types of the VM. @@ -37,6 +38,8 @@ enum PrimType : unsigned { PT_Uint32, PT_Sint64, PT_Uint64, + PT_IntAP, + PT_IntAPS, PT_Bool, PT_Float, PT_Ptr, @@ -68,6 +71,12 @@ template <> struct PrimConv<PT_Sint32> { using T = Integral<32, true>; }; template <> struct PrimConv<PT_Uint32> { using T = Integral<32, false>; }; template <> struct PrimConv<PT_Sint64> { using T = Integral<64, true>; }; template <> struct PrimConv<PT_Uint64> { using T = Integral<64, false>; }; +template <> struct PrimConv<PT_IntAP> { + using T = IntegralAP<false>; +}; +template <> struct PrimConv<PT_IntAPS> { + using T = IntegralAP<true>; +}; template <> struct PrimConv<PT_Float> { using T = Floating; }; template <> struct PrimConv<PT_Bool> { using T = Boolean; }; template <> struct PrimConv<PT_Ptr> { using T = Pointer; }; @@ -108,6 +117,8 @@ static inline bool aligned(const void *P) { TYPE_SWITCH_CASE(PT_Uint32, B) \ TYPE_SWITCH_CASE(PT_Sint64, B) \ TYPE_SWITCH_CASE(PT_Uint64, B) \ + TYPE_SWITCH_CASE(PT_IntAP, B) \ + TYPE_SWITCH_CASE(PT_IntAPS, B) \ TYPE_SWITCH_CASE(PT_Float, B) \ TYPE_SWITCH_CASE(PT_Bool, B) \ TYPE_SWITCH_CASE(PT_Ptr, B) \ @@ -126,6 +137,8 @@ static inline bool aligned(const void *P) { TYPE_SWITCH_CASE(PT_Uint32, B) \ TYPE_SWITCH_CASE(PT_Sint64, B) \ TYPE_SWITCH_CASE(PT_Uint64, B) \ + TYPE_SWITCH_CASE(PT_IntAP, B) \ + TYPE_SWITCH_CASE(PT_IntAPS, B) \ TYPE_SWITCH_CASE(PT_Bool, B) \ default: \ llvm_unreachable("Not an integer value"); \ diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp index ceda59405ea9105..0dd036353fa7efd 100644 --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -7,6 +7,8 @@ #define INT_MAX __INT_MAX__ typedef __INTPTR_TYPE__ intptr_t; +typedef __int128 int128_t; +typedef unsigned __int128 uint128_t; static_assert(true, ""); @@ -26,6 +28,66 @@ static_assert(number != 10, ""); // expected-error{{failed}} \ // expected-note{{evaluates to}} \ // ref-note{{evaluates to}} + +namespace i128 { + constexpr int128_t I128_1 = 12; + static_assert(I128_1 == 12, ""); + static_assert(I128_1 != 10, ""); + static_assert(I128_1 != 12, ""); // expected-error{{failed}} \ + // ref-error{{failed}} \ + // expected-note{{evaluates to}} \ + // ref-note{{evaluates to}} + + static const __uint128_t UINT128_MAX =__uint128_t(__int128_t(-1L)); + static_assert(UINT128_MAX == -1, ""); + + static const __int128_t INT128_MAX = UINT128_MAX >> (__int128_t)1; + static_assert(INT128_MAX != 0, ""); + static const __int128_t INT128_MIN = -INT128_MAX - 1; + constexpr __int128 A = INT128_MAX + 1; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{outside the range}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{outside the range}} + constexpr int128_t Two = (int128_t)1 << 1ul; + static_assert(Two == 2, ""); + +#if __cplusplus >= 201402L + template <typename T> + constexpr T CastFrom(__int128_t A) { + T B = (T)A; + return B; + } + static_assert(CastFrom<char>(12) == 12, ""); + static_assert(CastFrom<unsigned char>(12) == 12, ""); + static_assert(CastFrom<long>(12) == 12, ""); + static_assert(CastFrom<unsigned short>(12) == 12, ""); + static_assert(CastFrom<int128_t>(12) == 12, ""); + static_assert(CastFrom<float>(12) == 12, ""); + static_assert(CastFrom<double>(12) == 12, ""); + static_assert(CastFrom<long double>(12) == 12, ""); + + template <typename T> + constexpr __int128 CastTo(T A) { + int128_t B = (int128_t)A; + return B; + } + static_assert(CastTo<char>(12) == 12, ""); + static_assert(CastTo<unsigned char>(12) == 12, ""); + static_assert(CastTo<long>(12) == 12, ""); + static_assert(CastTo<unsigned long long>(12) == 12, ""); + static_assert(CastTo<float>(12) == 12, ""); + static_assert(CastTo<double>(12) == 12, ""); + static_assert(CastTo<long double>(12) == 12, ""); +#endif + +constexpr int128_t Error = __LDBL_MAX__; // ref-warning {{implicit conversion of out of range value}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{is outside the range of representable values of type}} \ + // expected-warning {{implicit conversion of out of range value}} \ + // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{is outside the range of representable values of type}} +} + constexpr bool b = number; static_assert(b, ""); constexpr int one = true; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits