diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index c81d17ed64108..7020f70f7c1b0 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -316,10 +316,14 @@ def note_constexpr_memcpy_unsupported : Note< "size to copy (%4) is not a multiple of size of element type %3 (%5)|" "source is not a contiguous array of at least %4 elements of type %3|" "destination is not a contiguous array of at least %4 elements of type %3}2">; +def note_constexpr_bit_cast_bad_bits : Note< + "bit_cast source expression (type %5) does not produce a constant value for " + "%select{bit|byte}0 [%1] (of {%2%plural{0:|:..0}2}) which are required by " + "target type %4 %select{|(subobject %3)}6">; def note_constexpr_bit_cast_unsupported_type : Note< "constexpr bit cast involving type %0 is not yet supported">; -def note_constexpr_bit_cast_unsupported_bitfield : Note< - "constexpr bit_cast involving bit-field is not yet supported">; +def note_constexpr_bit_cast_invalid_decl : Note< + "bit_cast here %select{from|to}0 invalid declaration %0">; def note_constexpr_bit_cast_invalid_type : Note< "bit_cast %select{from|to}0 a %select{|type with a }1" "%select{union|pointer|member pointer|volatile|reference}2 " diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 986302e1fd225..356cef552d354 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -38,7 +38,6 @@ #include "Interp/State.h" #include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" @@ -49,19 +48,33 @@ #include "clang/AST/OptionalDiagnostic.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" +#include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" -#include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/DiagnosticAST.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APFixedPoint.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallBitVector.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/SwapByteOrder.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" +#include +#include +#include #include #include +#include +#include #include #define DEBUG_TYPE "exprconstant" @@ -6901,51 +6914,113 @@ bool HandleOperatorDeleteCall(EvalInfo &Info, const CallExpr *E) { //===----------------------------------------------------------------------===// namespace { -class BitCastBuffer { - // FIXME: We're going to need bit-level granularity when we support - // bit-fields. +struct BitCastBuffer { // FIXME: Its possible under the C++ standard for 'char' to not be 8 bits, but // we don't support a host or target where that is the case. Still, we should // use a more generic type in case we ever do. - SmallVector, 32> Bytes; - - static_assert(std::numeric_limits::digits >= 8, + using byte_t = unsigned char; + static_assert(std::numeric_limits::digits >= 8, "Need at least 8 bit unsigned char"); + SmallVector Bytes; + SmallVector Valid; + bool TargetIsLittleEndian; -public: + static SmallVector MaskAllSet(size_t Width) { + SmallVector M; + M.resize(Width); + std::fill(M.begin(), M.end(), ~0); + return M; + } + BitCastBuffer(CharUnits Width, bool TargetIsLittleEndian) - : Bytes(Width.getQuantity()), + : Bytes(Width.getQuantity()), Valid(Width.getQuantity()), TargetIsLittleEndian(TargetIsLittleEndian) {} [[nodiscard]] bool readObject(CharUnits Offset, CharUnits Width, - SmallVectorImpl &Output) const { - for (CharUnits I = Offset, E = Offset + Width; I != E; ++I) { - // If a byte of an integer is uninitialized, then the whole integer is - // uninitialized. - if (!Bytes[I.getQuantity()]) + SmallVectorImpl &Output, + SmallVectorImpl const &Mask) const { + assert(Mask.size() >= static_cast(Width.getQuantity())); + assert(Output.size() >= static_cast(Width.getQuantity())); + assert(Bytes.size() >= + static_cast((Offset + Width).getQuantity())); + + SmallVector RevMask; + const SmallVectorImpl &M = + (llvm::sys::IsLittleEndianHost != TargetIsLittleEndian) + ? [&]() -> const SmallVectorImpl & { + auto W = Width.getQuantity(); + RevMask.resize_for_overwrite(W); + std::reverse_copy(Mask.begin(), Mask.begin() + W, RevMask.begin()); + return RevMask; + }() + : Mask; + + size_t Index = 0; + for (CharUnits I = Offset, E = Offset + Width; I != E; ++I, ++Index) { + const auto BufIdx = I.getQuantity(); + const auto mask = M[Index]; + // are there any bits in Mask[Index] that are not set in + // Valid[BufIdx]? (NB: more bits can be set, that's just + // fine) + if ((Valid[BufIdx] & M[Index]) != M[Index]) + // If any bit of an integer is uninitialized, then the + // whole integer is uninitialized. return false; - Output.push_back(*Bytes[I.getQuantity()]); + + Output[Index] = (Output[Index] & ~mask) | (Bytes[BufIdx] & mask); } + if (llvm::sys::IsLittleEndianHost != TargetIsLittleEndian) std::reverse(Output.begin(), Output.end()); return true; } - void writeObject(CharUnits Offset, SmallVectorImpl &Input) { - if (llvm::sys::IsLittleEndianHost != TargetIsLittleEndian) + void writeObject(CharUnits Offset, SmallVectorImpl &Input, + SmallVectorImpl &Mask) { + assert(Mask.size() >= Input.size()); + assert(Bytes.size() >= + static_cast(Offset.getQuantity()) + Input.size()); + + // we could promise Input and Mask were `const`, except for this + if (llvm::sys::IsLittleEndianHost != TargetIsLittleEndian) { std::reverse(Input.begin(), Input.end()); + // we might (will) have more mask bits than input bits + std::reverse(Mask.begin(), Mask.begin() + Input.size()); + } size_t Index = 0; - for (unsigned char Byte : Input) { - assert(!Bytes[Offset.getQuantity() + Index] && "overwriting a byte?"); - Bytes[Offset.getQuantity() + Index] = Byte; + size_t BufIdx = Offset.getQuantity(); + for (byte_t &Byte : Input) { + assert((Valid[BufIdx] & Mask[Index]) == 0 && "overwriting data?"); + Bytes[BufIdx] |= Byte & Mask[Index]; + Valid[BufIdx] |= Mask[Index]; + ++BufIdx; ++Index; } } size_t size() { return Bytes.size(); } + + LLVM_DUMP_METHOD void dump() { + auto pp = [](std::stringstream &SS, llvm::SmallVectorImpl &V) { + bool first = true; + for (byte_t v : V) { + if (first) + first = false; + else + SS << " "; + SS << "0x" << std::hex << std::setw(2) << std::setfill('0') + << static_cast(v); + } + }; + std::stringstream SS[2]; + pp(SS[0], Bytes); + pp(SS[1], Valid); + llvm::dbgs() << "BitCastBuffer{Bytes: [" << SS[0].str() << "], Valid: [" + << SS[1].str() << "]}\n"; + } }; /// Traverse an APValue to produce an BitCastBuffer, emulating how the current @@ -6973,7 +7048,7 @@ class APValueToBufferConverter { if (Ty->isNullPtrType()) return true; - // Dig through Src to find the byte at SrcOffset. + // Dig through Val to find the byte at Offset. switch (Val.getKind()) { case APValue::Indeterminate: case APValue::None: @@ -7012,6 +7087,9 @@ class APValueToBufferConverter { bool visitRecord(const APValue &Val, QualType Ty, CharUnits Offset) { const RecordDecl *RD = Ty->getAsRecordDecl(); + if (RD->isInvalidDecl()) { + return invalidDecl(Ty); + } const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); // Visit the base classes. @@ -7028,12 +7106,11 @@ class APValueToBufferConverter { // Visit the fields. unsigned FieldIdx = 0; - for (FieldDecl *FD : RD->fields()) { - if (FD->isBitField()) { - Info.FFDiag(BCE->getBeginLoc(), - diag::note_constexpr_bit_cast_unsupported_bitfield); - return false; - } + for (auto I = RD->field_begin(), E = RD->field_end(); I != E; + I++, FieldIdx++) { + FieldDecl *FD = *I; + if (FD->isBitField()) + continue; // see below uint64_t FieldOffsetBits = Layout.getFieldOffset(FieldIdx); @@ -7044,7 +7121,72 @@ class APValueToBufferConverter { QualType FieldTy = FD->getType(); if (!visit(Val.getStructField(FieldIdx), FieldTy, FieldOffset)) return false; - ++FieldIdx; + } + + // Handle bit-fields + FieldIdx = 0; + for (auto I = RD->field_begin(), E = RD->field_end(); I != E; + I++, FieldIdx++) { + FieldDecl *FD = *I; + if (!FD->isBitField()) + continue; + + // unnamed bit fields are purely padding + if (FD->isUnnamedBitfield()) + continue; + + auto FieldVal = Val.getStructField(FieldIdx); + if (!FieldVal.hasValue()) + continue; + + uint64_t FieldOffsetBits = Layout.getFieldOffset(FieldIdx); + CharUnits BufOffset = Offset; + uint64_t BitOffset = FieldOffsetBits; + + unsigned int BitWidth = FD->getBitWidthValue(Info.Ctx); + + CharUnits TypeWidth = Info.Ctx.getTypeSizeInChars(FD->getType()); + uint64_t TypeWidthBits = Info.Ctx.toBits(TypeWidth); + if (BitWidth > TypeWidthBits) { + // e.g. `unsigned uint8_t c : 12` + // we truncate to CHAR_BIT * sizeof(T) + // (the extra bits are padding) + BitWidth = TypeWidthBits; + } + if (FieldOffsetBits >= TypeWidthBits) { + // e.g. `uint32_t : 33; uint32_t i : 12` + // or `uint16_t : 16; unsigned uint16_t i : 12` + BufOffset = + BufOffset + CharUnits::fromQuantity(BitOffset / TypeWidthBits) * + TypeWidth.getQuantity(); + BitOffset %= TypeWidthBits; + } + + if (Info.Ctx.getTargetInfo().isBigEndian()) { + // big endian bits count from MSB to LSB + // so a bit-field of width 16 and size 12 will occupy bits [0-11] on a + // little endian machine, but [3-15] on a big endian machine + BitOffset = TypeWidthBits - (BitOffset + BitWidth); + } + + assert(TypeWidth >= Info.Ctx.toCharUnitsFromBits(BitWidth)); + + llvm::SmallBitVector MaskBits(Info.Ctx.toBits(TypeWidth)); + MaskBits.set(BitOffset, BitOffset + BitWidth); + uintptr_t Store; + ArrayRef Ref = MaskBits.getData(Store); + SmallVector Mask(Ref.size() * sizeof(uintptr_t)); + std::memcpy(Mask.data(), Ref.data(), Mask.size()); + Mask.truncate(TypeWidth.getQuantity()); + + SmallVector Bytes(TypeWidth.getQuantity()); + + APSInt Val = FieldVal.getInt() << BitOffset; + assert(Val.getBitWidth() >= BitOffset + BitWidth && + "lost data in APInt -> byte buffer conversion"); + + llvm::StoreIntToMemory(Val, &*Bytes.begin(), TypeWidth.getQuantity()); + Buffer.writeObject(BufOffset, Bytes, Mask); } return true; @@ -7129,8 +7271,9 @@ class APValueToBufferConverter { } SmallVector Bytes(NElts / 8); + auto Mask = BitCastBuffer::MaskAllSet(Bytes.size()); llvm::StoreIntToMemory(Res, &*Bytes.begin(), NElts / 8); - Buffer.writeObject(Offset, Bytes); + Buffer.writeObject(Offset, Bytes, Mask); } else { // Iterate over each of the elements and write them out to the buffer at // the appropriate offset. @@ -7153,8 +7296,9 @@ class APValueToBufferConverter { } SmallVector Bytes(Width / 8); + auto Mask = BitCastBuffer::MaskAllSet(Bytes.size()); llvm::StoreIntToMemory(AdjustedVal, &*Bytes.begin(), Width / 8); - Buffer.writeObject(Offset, Bytes); + Buffer.writeObject(Offset, Bytes, Mask); return true; } @@ -7163,6 +7307,12 @@ class APValueToBufferConverter { return visitInt(AsInt, Ty, Offset); } + bool invalidDecl(QualType Ty) { + Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_invalid_decl) + << /* checking dest */ false << Ty; + return false; + } + public: static std::optional convert(EvalInfo &Info, const APValue &Src, const CastExpr *BCE) { @@ -7194,6 +7344,12 @@ class BufferToAPValueConverter { return std::nullopt; } + std::nullopt_t invalidDecl(QualType Ty) { + Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_invalid_decl) + << /* checking dest */ true << Ty; + return std::nullopt; + } + std::nullopt_t unrepresentableValue(QualType Ty, const APSInt &Val) { Info.FFDiag(BCE->getBeginLoc(), diag::note_constexpr_bit_cast_unrepresentable_value) @@ -7201,6 +7357,75 @@ class BufferToAPValueConverter { return std::nullopt; } + std::nullopt_t badBits(QualType Ty, CharUnits Offset, + SmallVectorImpl &M) { + Info.FFDiag(BCE->getExprLoc(), diag::note_constexpr_bit_cast_indet_dest, 1) + << Ty << Info.Ctx.getLangOpts().CharIsSigned; + uint64_t BitWidth = Info.Ctx.getTypeSize(BCE->getType()); + uint64_t ByteWidth = Info.Ctx.toCharUnitsFromBits(BitWidth).getQuantity(); + assert(ByteWidth == Buffer.Valid.size_in_bytes()); + + APInt Valid(BitWidth, 0); + llvm::LoadIntFromMemory(Valid, Buffer.Valid.begin(), ByteWidth); + APInt Mask(BitWidth, 0); + llvm::LoadIntFromMemory(Mask, M.begin(), M.size_in_bytes()); + + Mask = Mask.zext(Valid.getBitWidth()); + Mask <<= Info.Ctx.toBits(Offset); + + auto ByteAligned = true; + + APInt Missing = (~Valid & Mask); + assert(!Missing.isZero() && "bad bits called with no bad bits?"); + llvm::SmallVector> MissingBitRanges; + int NextBit = 0; + while (!Missing.isZero()) { + APInt Last(Missing); + int N = Missing.countr_zero(); + + Missing.lshrInPlace(N); + auto M = Missing.countr_one(); + + MissingBitRanges.push_back({NextBit + N, NextBit + N + M}); + + Missing.lshrInPlace(M); + NextBit += N; + NextBit += M; + ByteAligned &= N % Info.Ctx.getCharWidth() == 0; + ByteAligned &= M % Info.Ctx.getCharWidth() == 0; + } + + llvm::SmallString<32> RangesStr; + llvm::raw_svector_ostream OS(RangesStr); + bool First = true; + for (auto [Start, End] : MissingBitRanges) { + if (!First) + OS << " "; + else + First = false; + if (ByteAligned) { + Start /= Info.Ctx.getCharWidth(); + End /= Info.Ctx.getCharWidth(); + } + size_t Len = End - Start; + if (Len > 1) { + OS << Start << "-" << End - 1; + } else { + OS << Start; + } + } + + assert(RangesStr.size() > 0); + auto LastIdx = (ByteAligned ? ByteWidth : BitWidth) - 1; + bool IsForSubobject = + BCE->getType().getCanonicalType() != Ty.getCanonicalType(); + Info.Note(BCE->getSubExpr()->getExprLoc(), + diag::note_constexpr_bit_cast_bad_bits) + << ByteAligned << RangesStr << LastIdx << Ty << BCE->getType() + << BCE->getSubExpr()->getType() << IsForSubobject; + return std::nullopt; + } + std::optional visit(const BuiltinType *T, CharUnits Offset, const EnumType *EnumSugar = nullptr) { if (T->isNullPtrType()) { @@ -7225,8 +7450,10 @@ class BufferToAPValueConverter { SizeOf = NumBytes; } - SmallVector Bytes; - if (!Buffer.readObject(Offset, SizeOf, Bytes)) { + SmallVector Bytes, + Mask = BitCastBuffer::MaskAllSet(SizeOf.getQuantity()); + Bytes.resize_for_overwrite(SizeOf.getQuantity()); + if (!Buffer.readObject(Offset, SizeOf, Bytes, Mask)) { // If this is std::byte or unsigned char, then its okay to store an // indeterminate value. bool IsStdByte = EnumSugar && EnumSugar->isStdByteType(); @@ -7235,10 +7462,7 @@ class BufferToAPValueConverter { T->isSpecificBuiltinType(BuiltinType::Char_U)); if (!IsStdByte && !IsUChar) { QualType DisplayType(EnumSugar ? (const Type *)EnumSugar : T, 0); - Info.FFDiag(BCE->getExprLoc(), - diag::note_constexpr_bit_cast_indet_dest) - << DisplayType << Info.Ctx.getLangOpts().CharIsSigned; - return std::nullopt; + return badBits(DisplayType, Offset, Mask); } return APValue::IndeterminateValue(); @@ -7272,6 +7496,9 @@ class BufferToAPValueConverter { std::optional visit(const RecordType *RTy, CharUnits Offset) { const RecordDecl *RD = RTy->getAsRecordDecl(); + if (RD->isInvalidDecl()) { + return invalidDecl(QualType(RD->getTypeForDecl(), 0)); + } const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); unsigned NumBases = 0; @@ -7300,14 +7527,11 @@ class BufferToAPValueConverter { // Visit the fields. unsigned FieldIdx = 0; - for (FieldDecl *FD : RD->fields()) { - // FIXME: We don't currently support bit-fields. A lot of the logic for - // this is in CodeGen, so we need to factor it around. - if (FD->isBitField()) { - Info.FFDiag(BCE->getBeginLoc(), - diag::note_constexpr_bit_cast_unsupported_bitfield); - return std::nullopt; - } + for (auto I = RD->field_begin(), E = RD->field_end(); I != E; + I++, FieldIdx++) { + FieldDecl *FD = *I; + if (FD->isBitField()) + continue; // see below uint64_t FieldOffsetBits = Layout.getFieldOffset(FieldIdx); assert(FieldOffsetBits % Info.Ctx.getCharWidth() == 0); @@ -7320,7 +7544,86 @@ class BufferToAPValueConverter { if (!SubObj) return std::nullopt; ResultVal.getStructField(FieldIdx) = *SubObj; - ++FieldIdx; + } + + // Handle bit-fields + FieldIdx = 0; + for (auto I = RD->field_begin(), E = RD->field_end(); I != E; + I++, FieldIdx++) { + FieldDecl *FD = *I; + if (!FD->isBitField()) + continue; + + // unnamed bit fields are purely padding + if (FD->isUnnamedBitfield()) + continue; + + uint64_t FieldOffsetBits = Layout.getFieldOffset(FieldIdx); + CharUnits BufOffset = Offset; + uint64_t BitOffset = FieldOffsetBits; + + unsigned int BitWidth = FD->getBitWidthValue(Info.Ctx); + + CharUnits TypeWidth = Info.Ctx.getTypeSizeInChars(FD->getType()); + uint64_t TypeWidthBits = Info.Ctx.toBits(TypeWidth); + if (BitWidth > TypeWidthBits) { + // e.g. `unsigned uint8_t c : 12` + // we truncate to CHAR_BIT * sizeof(T) + // (the extra bits are padding) + BitWidth = TypeWidthBits; + } + if (FieldOffsetBits >= TypeWidthBits) { + // e.g. `uint32_t : 33; uint32_t i : 12` + // or `uint16_t : 16; unsigned uint16_t i : 12` + BufOffset = + BufOffset + CharUnits::fromQuantity(BitOffset / TypeWidthBits) * + TypeWidth.getQuantity(); + BitOffset %= TypeWidthBits; + } + + if (Info.Ctx.getTargetInfo().isBigEndian()) { + // big endian bits count from MSB to LSB + // so a bit-field of width 16 and size 12 will occupy bits [0-11] on a + // little endian machine, but [3-15] on a big endian machine + BitOffset = TypeWidthBits - (BitOffset + BitWidth); + } + + assert(TypeWidth >= Info.Ctx.toCharUnitsFromBits(BitWidth)); + + llvm::SmallBitVector MaskBits(Info.Ctx.toBits(TypeWidth)); + MaskBits.set(BitOffset, BitOffset + BitWidth); + uintptr_t Store; + ArrayRef BitRef = MaskBits.getData(Store); + SmallVector Mask(BitRef.size() * sizeof(uintptr_t)); + std::memcpy(Mask.data(), BitRef.data(), Mask.size()); + Mask.truncate(TypeWidth.getQuantity()); + + SmallVector Bytes(TypeWidth.getQuantity()); + if (!Buffer.readObject(BufOffset, TypeWidth, Bytes, Mask)) { + const Type *T = FD->getType().getCanonicalType().getTypePtr(); + const EnumType *EnumSugar = dyn_cast(T); + // If this is std::byte or unsigned char, then its okay to store an + // indeterminate value. + bool IsStdByte = EnumSugar && EnumSugar->isStdByteType(); + bool IsUChar = + !EnumSugar && (T->isSpecificBuiltinType(BuiltinType::UChar) || + T->isSpecificBuiltinType(BuiltinType::Char_U)); + if (!IsStdByte && !IsUChar) { + QualType DisplayType(EnumSugar ? (const Type *)EnumSugar : T, 0); + return badBits(DisplayType, BufOffset, Mask); + } + ResultVal.getStructField(FieldIdx) = APValue::IndeterminateValue(); + } else { + APSInt Val(Info.Ctx.toBits(TypeWidth), true); + llvm::LoadIntFromMemory(Val, &*Bytes.begin(), TypeWidth.getQuantity()); + + Val >>= BitOffset; + Val = Val.trunc(BitWidth); + Val.setIsSigned(FD->getType()->isSignedIntegerOrEnumerationType()); + Val = Val.extend(Info.Ctx.toBits(TypeWidth)); + + ResultVal.getStructField(FieldIdx) = APValue(Val); + } } return ResultVal; @@ -7394,9 +7697,11 @@ class BufferToAPValueConverter { // actually need to be accessed. bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian(); - SmallVector Bytes; - Bytes.reserve(NElts / 8); - if (!Buffer.readObject(Offset, CharUnits::fromQuantity(NElts / 8), Bytes)) + size_t Width = NElts / 8; + SmallVector Bytes, Mask = BitCastBuffer::MaskAllSet(Width); + Bytes.resize_for_overwrite(Width); + if (!Buffer.readObject(Offset, CharUnits::fromQuantity(Width), Bytes, + Mask)) return std::nullopt; APSInt SValInt(NElts, true); diff --git a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp index c5b8032f40b13..29d046e2def3d 100644 --- a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp +++ b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp @@ -23,28 +23,24 @@ static_assert(sizeof(long long) == 8); template constexpr To bit_cast(const From &from) { static_assert(sizeof(To) == sizeof(From)); - // expected-note@+9 {{cannot be represented in type 'bool'}} -#ifdef __x86_64 - // expected-note@+7 {{or 'std::byte'; '__int128' is invalid}} -#endif -#ifdef __CHAR_UNSIGNED__ - // expected-note@+4 2 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'signed char' is invalid}} -#else - // expected-note@+2 2 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'signed char' is invalid}} -#endif return __builtin_bit_cast(To, from); } template -constexpr bool round_trip(const Init &init) { +constexpr bool check_round_trip(const Init &init) { return bit_cast(bit_cast(init)) == init; } +template +constexpr Init round_trip(const Init &init) { + return bit_cast(bit_cast(init)); +} + void test_int() { - static_assert(round_trip((int)-1)); - static_assert(round_trip((int)0x12345678)); - static_assert(round_trip((int)0x87654321)); - static_assert(round_trip((int)0x0C05FEFE)); + static_assert(check_round_trip((int)-1)); + static_assert(check_round_trip((int)0x12345678)); + static_assert(check_round_trip((int)0x87654321)); + static_assert(check_round_trip((int)0x0C05FEFE)); } void test_array() { @@ -73,8 +69,8 @@ void test_record() { ? 0x0C05FEFE : 0xCAFEBABE)); - static_assert(round_trip(splice)); - static_assert(round_trip(splice)); + static_assert(round_trip(splice) == splice); + static_assert(round_trip(splice) == splice); struct base2 { }; @@ -98,7 +94,7 @@ void test_record() { constexpr bases b = {{1, 2}, {}, {3}, 4}; constexpr tuple4 t4 = bit_cast(b); static_assert(t4 == tuple4{1, 2, 3, 4}); - static_assert(round_trip(b)); + static_assert(round_trip(b) == b); } void test_partially_initialized() { @@ -115,33 +111,295 @@ void test_partially_initialized() { static_assert(sizeof(pad) == sizeof(no_pad)); + constexpr auto cast = [](const pad& from) constexpr { + // expected-note@+6 2 {{bit_cast source expression (type 'const pad') does not produce a constant value for byte [1] (of {7..0}) which are required by target type 'no_pad' (subobject 'signed char')}} + #ifdef __CHAR_UNSIGNED__ + // expected-note@+4 2 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'signed char' is invalid}} + #else + // expected-note@+2 2 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'signed char' is invalid}} + #endif + return __builtin_bit_cast(no_pad, from); + }; + constexpr pad pir{4, 4}; // expected-error@+2 {{constexpr variable 'piw' must be initialized by a constant expression}} - // expected-note@+1 {{in call to 'bit_cast(pir)'}} - constexpr int piw = bit_cast(pir).x; + // expected-note@+1 {{in call}} + constexpr int piw = cast(pir).x; // expected-error@+2 {{constexpr variable 'bad' must be initialized by a constant expression}} - // expected-note@+1 {{in call to 'bit_cast(pir)'}} - constexpr no_pad bad = bit_cast(pir); + // expected-note@+1 {{in call}} + constexpr no_pad bad = cast(pir); constexpr pad fine = bit_cast(no_pad{1, 2, 3, 4, 5}); static_assert(fine.x == 1 && fine.y == 5); } -void no_bitfields() { - // FIXME! +namespace std { +enum byte : unsigned char {}; +} // namespace std + +template +struct bits { + T : Pad; + T bits : N; + + constexpr bool operator==(const T& rhs) const { + return bits == rhs; + } +}; + +template +constexpr bool operator==(const struct bits& lhs, const struct bits& rhs) { + return lhs.bits == rhs.bits; +} + +void test_bitfields() { + using uint16_t = unsigned __INT16_TYPE__; + { + struct Q { + // cf. CGBitFieldInfo + // on a little-endian machine the bits "[count from] the + // least-significant-bit." + // so, by leaving a bit unused, we truncate the value's MSB. + + // however, on a big-endian machine we "imagine the bits + // counting from the most-significant-bit", so we truncate + // the LSB here. + uint16_t q : 15; + }; + constexpr unsigned char bits[2] = {0xf3, 0xef}; + constexpr Q q = bit_cast(bits); + static_assert(bit_cast(bits) == (LITTLE_END + ? 0xeff3 + : 0xf3ef), + "bit-field casting ought to match \"whole\"-field casting"); + static_assert(q.q == (LITTLE_END ? 0x6ff3 : (0xf3ee >> 1))); + } + struct S { - unsigned char x : 8; + // little endian: + // MSB .... .... LSB + // |y| |x| + // + // big endian + // MSB .... .... LSB + // |x| |y| + + unsigned char x : 4; + unsigned char y : 4; + + constexpr bool operator==(S const &other) const { + return x == other.x && y == other.y; + } }; - struct G { - unsigned char x : 8; + constexpr S s{0xa, 0xb}; + static_assert(bit_cast>(s) == (LITTLE_END ? 0xba : 0xab)); + static_assert(bit_cast>(s) == (LITTLE_END + ? 0xba & 0x7f + : (0xab & 0xfe) >> 1)); + + static_assert(round_trip>(s) == s); + + struct R { + unsigned int r : 31; + unsigned int : 0; + unsigned int : 32; + constexpr bool operator==(R const &other) const { + return r == other.r; + } }; + using T = bits<31, signed long long>; - constexpr S s{0}; - // expected-error@+2 {{constexpr variable 'g' must be initialized by a constant expression}} - // expected-note@+1 {{constexpr bit_cast involving bit-field is not yet supported}} - constexpr G g = __builtin_bit_cast(G, s); + constexpr R r{0x4ac0ffee}; + constexpr T t = bit_cast(r); + static_assert(t == ((0xFFFFFFFF8 << 28) | 0x4ac0ffee)); // sign extension + + static_assert(round_trip(r) == r); + static_assert(round_trip(t) == t); + + struct U { + // expected-warning@+1 {{exceeds the width of its type}} + unsigned __INT32_TYPE__ trunc : 33; + unsigned __INT32_TYPE__ u : 31; + constexpr bool operator==(U const &other) const { + return trunc == other.trunc && u == other.u; + } + }; + struct V { + unsigned __INT64_TYPE__ notrunc : 32; + unsigned __INT64_TYPE__ : 1; + unsigned __INT64_TYPE__ v : 31; + constexpr bool operator==(V const &other) const { + return notrunc == other.notrunc && v == other.v; + } + }; + + constexpr U u{static_cast(~0), 0x4ac0ffee}; + constexpr V v = bit_cast(u); + static_assert(v.v == 0x4ac0ffee); + + { + #define MSG "a constexpr ought to produce padding bits from padding bits" + static_assert(round_trip(u) == u, MSG); + static_assert(round_trip(v) == v, MSG); + + constexpr auto w = bit_cast>(u); + static_assert(w == (LITTLE_END + ? 0x4ac0ffee & 0xFFF + : (0x4ac0ffee & (0xFFF << (31 - 12))) >> (31-12) + ), MSG); + #undef MSG + } + + // nested structures + { + struct J { + struct { + uint16_t k : 12; + } K; + struct { + uint16_t l : 4; + } L; + }; + + static_assert(sizeof(J) == 4); + constexpr J j = bit_cast(0x8c0ffee5); + + static_assert(j.K.k == (LITTLE_END ? 0xee5 : 0x8c0)); + static_assert(j.L.l == 0xf /* yay symmetry */); + static_assert(bit_cast>(j) == 0xf); + struct N { + bits<12, uint16_t> k; + uint16_t : 16; + }; + static_assert(bit_cast(j).k == j.K.k); + + struct M { + bits<4, uint16_t, 0> m[2]; + constexpr bool operator==(const M& rhs) const { + return m[0] == rhs.m[0] && m[1] == rhs.m[1]; + }; + }; + #if LITTLE_END == 1 + constexpr uint16_t want[2] = {0x5, 0xf}; + #else + constexpr uint16_t want[2] = {0x8000, 0xf000}; + #endif + + static_assert(bit_cast(j) == bit_cast(want)); + } + + // enums + { + // ensure we're packed into the top 2 bits + constexpr int pad = LITTLE_END ? 6 : 0; + struct X + { + char : pad; + enum class direction: char { left, right, up, down } direction : 2; + }; + + constexpr X x = { X::direction::down }; + static_assert(bit_cast>(x) == -1); + static_assert(bit_cast>(x) == 3); + static_assert( + bit_cast((unsigned char)0x40).direction == X::direction::right); + } +} + +template +struct bytebuf { + using size_t = int; + unsigned char bytes[N]; + + constexpr unsigned char &operator[](size_t index) { + if (index < N) + return bytes[index]; + } +}; + +void bitfield_indeterminate() { + struct BF { unsigned char z : 2; }; + enum byte : unsigned char {}; + + constexpr BF bf = {0x3}; + static_assert(bit_cast>(bf).bits == bf.z); + + // expected-error@+1 {{not an integral constant expression}} + static_assert(bit_cast(bf)); + /// FIXME the above doesn't get any helpful notes, but the below does +#if LITTLE_END == 1 + // expected-note@+6 {{bit [2-7]}} +#else + // expected-note@+4 {{bit [0-5]}} +#endif + // expected-note@+2 {{indeterminate}} + // expected-error@+1 {{not an integral constant expression}} + static_assert(__builtin_bit_cast(byte, bf)); + + struct M { + // expected-note@+1 {{subobject declared here}} + unsigned char mem[sizeof(BF)]; + }; + // expected-error@+3 {{initialized by a constant expression}} + // zzexpected-note@+2 {{bad bits}} + // expected-note@+1 {{not initialized}} + constexpr M m = bit_cast(bf); + + constexpr auto f = []() constexpr { + // bits<24, unsigned int, LITTLE_END ? 0 : 8> B = {0xc0ffee}; + constexpr struct { unsigned short b1; unsigned char b0; } B = {0xc0ff, 0xee}; + return bit_cast>(B); + }; + + static_assert(f()[0] + f()[1] + f()[2] == 0xc0 + 0xff + 0xee); + { + // expected-error@+2 {{initialized by a constant expression}} + // expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}} + constexpr auto _bad = f()[3]; + } + + struct B { + unsigned short s0 : 8; + unsigned short s1 : 8; + std::byte b0 : 4; + std::byte b1 : 4; + std::byte b2 : 4; + }; + constexpr auto g = [f]() constexpr { + return bit_cast(f()); + }; + static_assert(g().s0 + g().s1 + g().b0 + g().b1 == 0xc0 + 0xff + 0xe + 0xe); + { + // expected-error@+2 {{initialized by a constant expression}} + // expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}} + constexpr auto _bad = g().b2; + } +} + +void bitfield_unsupported() { + // if a future standard requires more types to be permitted in the + // declaration of a bit-field, then this test will hopefully indicate + // that there's work to be done on __builtin_bit_cast. + struct U { + // expected-error@+1 {{bit-field 'f' has non-integral type}} + bool f[8] : 8; + }; + + // this next bit is speculative: if the above _were_ a valid definition, + // then the below might also be a reasonable interpretation of its + // semantics, but the current implementation of __builtin_bit_cast will + // fail + + // expected-note@+3 {{invalid declaration}} FIXME should we instead bail out in Sema? + // expected-note@+2 {{declared here}} + // expected-error@+1 {{initialized by a constant expression}} + constexpr U u = __builtin_bit_cast(U, (char)0b1010'0101); + static_assert(U.f[0] && U.f[2] && U.f[4] && U.f[8]); + // expected-note@+2 {{not a constant expression}} + // expected-error@+1 {{not an integral constant expression}} + static_assert(__builtin_bit_cast(bits<8>, u) == 0xA5); } void array_members() { @@ -165,8 +423,8 @@ void array_members() { constexpr G g = bit_cast(s); static_assert(g.a == 1 && g.b == 2 && g.c == 3); - static_assert(round_trip(s)); - static_assert(round_trip(g)); + static_assert(check_round_trip(s)); + static_assert(check_round_trip(g)); } void bad_types() { @@ -229,6 +487,7 @@ void test_array_fill() { typedef decltype(nullptr) nullptr_t; +// expected-note@+7 {{byte [0-7]}} #ifdef __CHAR_UNSIGNED__ // expected-note@+5 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'unsigned long' is invalid}} #else @@ -350,10 +609,6 @@ constexpr A two() { } constexpr short good_two = two().c + two().s; -namespace std { -enum byte : unsigned char {}; -} - enum my_byte : unsigned char {}; struct pad { @@ -364,16 +619,18 @@ struct pad { constexpr int ok_byte = (__builtin_bit_cast(std::byte[8], pad{1, 2}), 0); constexpr int ok_uchar = (__builtin_bit_cast(unsigned char[8], pad{1, 2}), 0); +// expected-note@+7 {{bit_cast source expression (type 'pad') does not produce a constant value for byte [1] (of {7..0}) which are required by target type 'my_byte[8]' (subobject 'my_byte')}} #ifdef __CHAR_UNSIGNED__ -// expected-note@+5 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'my_byte' is invalid}}}} +// expected-note@+5 {{indeterminate value can only initialize an object of type 'unsigned char', 'char', or 'std::byte'; 'my_byte' is invalid}} #else // expected-note@+3 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'my_byte' is invalid}} #endif // expected-error@+1 {{constexpr variable 'bad_my_byte' must be initialized by a constant expression}} constexpr int bad_my_byte = (__builtin_bit_cast(my_byte[8], pad{1, 2}), 0); #ifndef __CHAR_UNSIGNED__ -// expected-error@+3 {{constexpr variable 'bad_char' must be initialized by a constant expression}} -// expected-note@+2 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'char' is invalid}} +// expected-note@+4 {{bit_cast source expression (type 'pad') does not produce a constant value for byte [1] (of {7..0}) which are required by target type 'char[8]' (subobject 'char')}} +// expected-note@+3 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'char' is invalid}} +// expected-error@+2 {{constexpr variable 'bad_char' must be initialized by a constant expression}} #endif constexpr int bad_char = (__builtin_bit_cast(char[8], pad{1, 2}), 0); @@ -404,19 +661,22 @@ constexpr unsigned char identity3b = __builtin_bit_cast(unsigned char, identity3 namespace test_bool { -constexpr bool test_bad_bool = bit_cast('A'); // expected-error {{must be initialized by a constant expression}} expected-note{{in call}} +// expected-note@+1 {{cannot be represented in type 'bool'}} +constexpr bool test_bad_bool = __builtin_bit_cast(bool, 'A'); // expected-error {{must be initialized by a constant expression}} -static_assert(round_trip(true), ""); -static_assert(round_trip(false), ""); -static_assert(round_trip(false), ""); +static_assert(check_round_trip(true)); +static_assert(check_round_trip(false)); +static_assert(check_round_trip(false)); -static_assert(round_trip((char)0), ""); -static_assert(round_trip((char)1), ""); +static_assert(check_round_trip((char)0)); +static_assert(check_round_trip((char)1)); } namespace test_long_double { #ifdef __x86_64 -constexpr __int128_t test_cast_to_int128 = bit_cast<__int128_t>((long double)0); // expected-error{{must be initialized by a constant expression}} expected-note{{in call}} +// expected-note@+2 {{byte [10-15]}} +// expected-note@+1 {{or 'std::byte'; '__int128' is invalid}} +constexpr __int128_t test_cast_to_int128 = __builtin_bit_cast(__int128_t, (long double)0); // expected-error{{must be initialized by a constant expression}} constexpr long double ld = 3.1425926539; @@ -424,9 +684,9 @@ struct bytes { unsigned char d[16]; }; -static_assert(round_trip(ld), ""); +static_assert(check_round_trip(ld)); -static_assert(round_trip(10.0L)); +static_assert(check_round_trip(10.0L)); constexpr bool f(bool read_uninit) { bytes b = bit_cast(ld); @@ -445,8 +705,8 @@ constexpr bool f(bool read_uninit) { return true; } -static_assert(f(/*read_uninit=*/false), ""); -static_assert(f(/*read_uninit=*/true), ""); // expected-error{{static assertion expression is not an integral constant expression}} expected-note{{in call to 'f(true)'}} +static_assert(f(/*read_uninit=*/false)); +static_assert(f(/*read_uninit=*/true)); // expected-error{{static assertion expression is not an integral constant expression}} expected-note{{in call to 'f(true)'}} constexpr bytes ld539 = { 0x0, 0x0, 0x0, 0x0, @@ -457,7 +717,7 @@ constexpr bytes ld539 = { constexpr long double fivehundredandthirtynine = 539.0; -static_assert(bit_cast(ld539) == fivehundredandthirtynine, ""); +static_assert(bit_cast(ld539) == fivehundredandthirtynine); #else static_assert(round_trip<__int128_t>(34.0L)); @@ -473,10 +733,10 @@ constexpr uint2 test_vector = { 0x0C05FEFE, 0xCAFEBABE }; static_assert(bit_cast(test_vector) == (LITTLE_END ? 0xCAFEBABE0C05FEFE - : 0x0C05FEFECAFEBABE), ""); + : 0x0C05FEFECAFEBABE)); -static_assert(round_trip(0xCAFEBABE0C05FEFEULL), ""); -static_assert(round_trip(0xCAFEBABE0C05FEFEULL), ""); +static_assert(check_round_trip(0xCAFEBABE0C05FEFEULL)); +static_assert(check_round_trip(0xCAFEBABE0C05FEFEULL)); typedef bool bool8 __attribute__((ext_vector_type(8))); typedef bool bool9 __attribute__((ext_vector_type(9))); @@ -485,16 +745,16 @@ typedef bool bool17 __attribute__((ext_vector_type(17))); typedef bool bool32 __attribute__((ext_vector_type(32))); typedef bool bool128 __attribute__((ext_vector_type(128))); -static_assert(bit_cast(bool8{1,0,1,0,1,0,1,0}) == (LITTLE_END ? 0x55 : 0xAA), ""); -static_assert(round_trip(static_cast(0)), ""); -static_assert(round_trip(static_cast(1)), ""); -static_assert(round_trip(static_cast(0x55)), ""); +static_assert(bit_cast(bool8{1,0,1,0,1,0,1,0}) == (LITTLE_END ? 0x55 : 0xAA)); +static_assert(check_round_trip(static_cast(0))); +static_assert(check_round_trip(static_cast(1))); +static_assert(check_round_trip(static_cast(0x55))); -static_assert(bit_cast(bool16{1,1,1,1,1,0,0,0, 1,1,1,1,0,1,0,0}) == (LITTLE_END ? 0x2F1F : 0xF8F4), ""); +static_assert(bit_cast(bool16{1,1,1,1,1,0,0,0, 1,1,1,1,0,1,0,0}) == (LITTLE_END ? 0x2F1F : 0xF8F4)); -static_assert(round_trip(static_cast(0xCAFE)), ""); -static_assert(round_trip(static_cast(0xCAFEBABE)), ""); -static_assert(round_trip(static_cast<__int128_t>(0xCAFEBABE0C05FEFEULL)), ""); +static_assert(check_round_trip(static_cast(0xCAFE))); +static_assert(check_round_trip(static_cast(0xCAFEBABE))); +static_assert(check_round_trip(static_cast<__int128_t>(0xCAFEBABE0C05FEFEULL))); // expected-error@+2 {{constexpr variable 'bad_bool9_to_short' must be initialized by a constant expression}} // expected-note@+1 {{bit_cast involving type 'bool __attribute__((ext_vector_type(9)))' (vector of 9 'bool' values) is not allowed in a constant expression; element size 1 * element count 9 is not a multiple of the byte size 8}} diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp index 792dc78464b2a..f6fbea4ab03d7 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp @@ -216,15 +216,19 @@ namespace UnnamedBitfield { // uninitialized and it being zeroed. Those are not distinct states // according to [temp.type]p2. // - // FIXME: We shouldn't track a value for unnamed bit-fields, nor number - // them when computing field indexes. + // At namespace scope, multiple `using` declarations are valid (to avoid + // conflicts when #including), just in case they they "all refer to the same + // entity." This test makes use of that implicit constraint to ensure that + // the compiler does not "see" a difference between any of the `T`s below. + // cf. https://stackoverflow.com/a/31225016/151464 template struct X {}; constexpr A a; using T = X; using T = X; using T = X<(A())>; - // Once we support bit-casts involving bit-fields, this should be valid too. - using T = X<__builtin_bit_cast(A, 0)>; // expected-error {{constant}} expected-note {{not yet supported}} + using T = X<__builtin_bit_cast(A, 0)>; + using T = X<__builtin_bit_cast(A, A{})>; + using T = X<__builtin_bit_cast(A, (unsigned char[4]){})>; } namespace Temporary {