From 159cd770e3ea0c5cba37006c4dc96b3235670f0d Mon Sep 17 00:00:00 2001 From: Erik Pilkington Date: Tue, 2 Jul 2019 18:28:13 +0000 Subject: [PATCH] [C++2a] Add __builtin_bit_cast, used to implement std::bit_cast This commit adds a new builtin, __builtin_bit_cast(T, v), which performs a bit_cast from a value v to a type T. This expression can be evaluated at compile time under specific circumstances. The compile time evaluation currently doesn't support bit-fields, but I'm planning on fixing this in a follow up (some of the logic for figuring this out is in CodeGen). I'm also planning follow-ups for supporting some more esoteric types that the constexpr evaluator supports, as well as extending __builtin_memcpy constexpr evaluation to use the same infrastructure. rdar://44987528 Differential revision: https://reviews.llvm.org/D62825 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@364954 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 6 +- include/clang/AST/ExprCXX.h | 29 ++ include/clang/AST/OperationKinds.def | 4 + include/clang/AST/RecursiveASTVisitor.h | 4 + include/clang/Basic/DiagnosticASTKinds.td | 13 + include/clang/Basic/DiagnosticSemaKinds.td | 4 + include/clang/Basic/StmtNodes.td | 1 + include/clang/Basic/TokenKinds.def | 2 +- include/clang/Parse/Parser.h | 3 + include/clang/Sema/Sema.h | 7 + lib/AST/Expr.cpp | 4 +- lib/AST/ExprClassification.cpp | 1 + lib/AST/ExprConstant.cpp | 511 ++++++++++++++++++- lib/AST/ItaniumMangle.cpp | 1 + lib/AST/StmtPrinter.cpp | 8 + lib/AST/StmtProfile.cpp | 5 + lib/CodeGen/CGExpr.cpp | 1 + lib/CodeGen/CGExprAgg.cpp | 19 + lib/CodeGen/CGExprComplex.cpp | 9 + lib/CodeGen/CGExprConstant.cpp | 1 + lib/CodeGen/CGExprScalar.cpp | 9 + lib/Edit/RewriteObjCFoundationAPI.cpp | 1 + lib/Lex/PPMacroExpansion.cpp | 1 + lib/Parse/ParseExpr.cpp | 3 + lib/Parse/ParseExprCXX.cpp | 34 ++ lib/Sema/SemaCast.cpp | 70 +++ lib/Sema/SemaExceptionSpec.cpp | 1 + lib/Sema/TreeTransform.h | 26 + lib/Serialization/ASTReaderStmt.cpp | 6 + lib/Serialization/ASTWriterStmt.cpp | 6 + lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + lib/StaticAnalyzer/Core/ExprEngineC.cpp | 1 + test/CodeGenCXX/builtin-bit-cast-no-tbaa.cpp | 19 + test/CodeGenCXX/builtin-bit-cast.cpp | 106 ++++ test/SemaCXX/builtin-bit-cast.cpp | 39 ++ test/SemaCXX/constexpr-builtin-bit-cast.cpp | 383 ++++++++++++++ tools/libclang/CIndex.cpp | 2 + tools/libclang/CXCursor.cpp | 2 + 38 files changed, 1334 insertions(+), 9 deletions(-) create mode 100644 test/CodeGenCXX/builtin-bit-cast-no-tbaa.cpp create mode 100644 test/CodeGenCXX/builtin-bit-cast.cpp create mode 100644 test/SemaCXX/builtin-bit-cast.cpp create mode 100644 test/SemaCXX/constexpr-builtin-bit-cast.cpp diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 07e71bd48d..74badac740 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -2546,7 +2546,11 @@ enum CXCursorKind { */ CXCursor_OMPTargetTeamsDistributeSimdDirective = 279, - CXCursor_LastStmt = CXCursor_OMPTargetTeamsDistributeSimdDirective, + /** C++2a std::bit_cast expression. + */ + CXCursor_BuiltinBitCastExpr = 280, + + CXCursor_LastStmt = CXCursor_BuiltinBitCastExpr, /** * Cursor that represents the translation unit itself. diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 551a677570..28ed6cdfde 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -4716,6 +4716,35 @@ public: } }; +/// Represents a C++2a __builtin_bit_cast(T, v) expression. Used to implement +/// std::bit_cast. These can sometimes be evaluated as part of a constant +/// expression, but otherwise CodeGen to a simple memcpy in general. +class BuiltinBitCastExpr final + : public ExplicitCastExpr, + private llvm::TrailingObjects { + friend class ASTStmtReader; + friend class CastExpr; + friend class TrailingObjects; + + SourceLocation KWLoc; + SourceLocation RParenLoc; + +public: + BuiltinBitCastExpr(QualType T, ExprValueKind VK, CastKind CK, Expr *SrcExpr, + TypeSourceInfo *DstType, SourceLocation KWLoc, + SourceLocation RParenLoc) + : ExplicitCastExpr(BuiltinBitCastExprClass, T, VK, CK, SrcExpr, 0, + DstType), + KWLoc(KWLoc), RParenLoc(RParenLoc) {} + + SourceLocation getBeginLoc() const LLVM_READONLY { return KWLoc; } + SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == BuiltinBitCastExprClass; + } +}; + } // namespace clang #endif // LLVM_CLANG_AST_EXPRCXX_H diff --git a/include/clang/AST/OperationKinds.def b/include/clang/AST/OperationKinds.def index c61f085a58..9af92c1ae7 100644 --- a/include/clang/AST/OperationKinds.def +++ b/include/clang/AST/OperationKinds.def @@ -66,6 +66,10 @@ CAST_OPERATION(BitCast) /// bool b; reinterpret_cast(b) = 'a'; CAST_OPERATION(LValueBitCast) +/// CK_LValueToRValueBitCast - A conversion that causes us to reinterpret an +/// lvalue as an rvalue of a different type. Created by __builtin_bit_cast. +CAST_OPERATION(LValueToRValueBitCast) + /// CK_LValueToRValue - A conversion which causes the extraction of /// an r-value from the operand gl-value. The result of an r-value /// conversion is always unqualified. diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index f3d8a889d6..292b57af76 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -2282,6 +2282,10 @@ DEF_TRAVERSE_STMT(CXXStaticCastExpr, { TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc())); }) +DEF_TRAVERSE_STMT(BuiltinBitCastExpr, { + TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc())); +}) + template bool RecursiveASTVisitor::TraverseSynOrSemInitListExpr( InitListExpr *S, DataRecursionQueue *Queue) { diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 3e7aeeaf0d..23502152b4 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -215,6 +215,19 @@ 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_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_type : Note< + "bit_cast %select{from|to}0 a %select{|type with a }1" + "%select{union|pointer|member pointer|volatile|reference}2 " + "%select{type|member}1 is not allowed in a constant expression">; +def note_constexpr_bit_cast_invalid_subtype : Note< + "invalid type %0 is a %select{member|base}1 of %2">; +def note_constexpr_bit_cast_indet_dest : Note< + "indeterminate value can only initialize an object of type 'unsigned char'" + "%select{, 'char',|}1 or 'std::byte'; %0 is invalid">; def warn_integer_constant_overflow : Warning< "overflow in expression; result is %0 with type %1">, diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index b9d081c7e2..ef78f44d14 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -9734,4 +9734,8 @@ def err_builtin_launder_invalid_arg : Error< "%select{non-pointer|function pointer|void pointer}0 argument to " "'__builtin_launder' is not allowed">; +def err_bit_cast_non_trivially_copyable : Error< + "__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">; +def err_bit_cast_type_size_mismatch : Error< + "__builtin_bit_cast source size does not equal destination size (%0 vs %1)">; } // end of sema component. diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index 358b318c35..be364de1a7 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -192,6 +192,7 @@ def ConvertVectorExpr : DStmt; def BlockExpr : DStmt; def OpaqueValueExpr : DStmt; def TypoExpr : DStmt; +def BuiltinBitCastExpr : DStmt; // Microsoft Extensions. def MSPropertyRefExpr : DStmt; diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index b185923359..764f586f14 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -670,7 +670,7 @@ ALIAS("_pascal" , __pascal , KEYBORLAND) KEYWORD(__builtin_convertvector , KEYALL) ALIAS("__char16_t" , char16_t , KEYCXX) ALIAS("__char32_t" , char32_t , KEYCXX) - +KEYWORD(__builtin_bit_cast , KEYALL) KEYWORD(__builtin_available , KEYALL) // Clang-specific keywords enabled only in testing. diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 4e40685d07..32bbf4dcd5 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1780,6 +1780,9 @@ private: // C++ 5.2p1: C++ Casts ExprResult ParseCXXCasts(); + /// Parse a __builtin_bit_cast(T, E), used to implement C++2a std::bit_cast. + ExprResult ParseBuiltinBitCast(); + //===--------------------------------------------------------------------===// // C++ 5.2p1: C++ Type Identification ExprResult ParseCXXTypeid(); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 27e7420bf2..4cb04746bf 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -5236,6 +5236,13 @@ public: SourceRange AngleBrackets, SourceRange Parens); + ExprResult ActOnBuiltinBitCastExpr(SourceLocation KWLoc, Declarator &Dcl, + ExprResult Operand, + SourceLocation RParenLoc); + + ExprResult BuildBuiltinBitCastExpr(SourceLocation KWLoc, TypeSourceInfo *TSI, + Expr *Operand, SourceLocation RParenLoc); + ExprResult BuildCXXTypeId(QualType TypeInfoType, SourceLocation TypeidLoc, TypeSourceInfo *Operand, diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index d342bda962..f8017bb7ad 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1862,6 +1862,7 @@ bool CastExpr::CastConsistency() const { case CK_FloatingComplexToBoolean: case CK_IntegralComplexToBoolean: case CK_LValueBitCast: // -> bool& + case CK_LValueToRValueBitCast: case CK_UserDefinedConversion: // operator bool() case CK_BuiltinFnToFnPtr: case CK_FixedPointToBoolean: @@ -3506,7 +3507,8 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case CXXStaticCastExprClass: case CXXReinterpretCastExprClass: case CXXConstCastExprClass: - case CXXFunctionalCastExprClass: { + case CXXFunctionalCastExprClass: + case BuiltinBitCastExprClass: { // While volatile reads are side-effecting in both C and C++, we treat them // as having possible (not definite) side-effects. This allows idiomatic // code to behave without warning, such as sizeof(*v) for a volatile- diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index 757a17660a..c61ee703ac 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -343,6 +343,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CXXReinterpretCastExprClass: case Expr::CXXConstCastExprClass: case Expr::ObjCBridgedCastExprClass: + case Expr::BuiltinBitCastExprClass: // Only in C++ can casts be interesting at all. if (!Lang.CPlusPlus) return Cl::CL_PRValue; return ClassifyUnnamed(Ctx, cast(E)->getTypeAsWritten()); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index df7dd19178..a71cc8e123 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -47,6 +47,7 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/FixedPoint.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" @@ -56,8 +57,10 @@ #define DEBUG_TYPE "exprconstant" using namespace clang; +using llvm::APInt; using llvm::APSInt; using llvm::APFloat; +using llvm::Optional; static bool IsGlobalLValue(APValue::LValueBase B); @@ -2657,10 +2660,6 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E, return true; } -static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, - QualType Type, const LValue &LVal, - APValue &RVal); - /// Try to evaluate the initializer for a variable declaration. /// /// \param Info Information about the ongoing evaluation. @@ -5376,6 +5375,489 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, //===----------------------------------------------------------------------===// namespace { +class BitCastBuffer { + // FIXME: We're going to need bit-level granularity when we support + // bit-fields. + // 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, + "Need at least 8 bit unsigned char"); + + bool TargetIsLittleEndian; + +public: + BitCastBuffer(CharUnits Width, bool TargetIsLittleEndian) + : Bytes(Width.getQuantity()), + TargetIsLittleEndian(TargetIsLittleEndian) {} + + LLVM_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 + // uninitalized. + if (!Bytes[I.getQuantity()]) + return false; + Output.push_back(*Bytes[I.getQuantity()]); + } + if (llvm::sys::IsLittleEndianHost != TargetIsLittleEndian) + std::reverse(Output.begin(), Output.end()); + return true; + } + + void writeObject(CharUnits Offset, SmallVectorImpl &Input) { + if (llvm::sys::IsLittleEndianHost != TargetIsLittleEndian) + std::reverse(Input.begin(), Input.end()); + + size_t Index = 0; + for (unsigned char Byte : Input) { + assert(!Bytes[Offset.getQuantity() + Index] && "overwriting a byte?"); + Bytes[Offset.getQuantity() + Index] = Byte; + ++Index; + } + } + + size_t size() { return Bytes.size(); } +}; + +/// Traverse an APValue to produce an BitCastBuffer, emulating how the current +/// target would represent the value at runtime. +class APValueToBufferConverter { + EvalInfo &Info; + BitCastBuffer Buffer; + const CastExpr *BCE; + + APValueToBufferConverter(EvalInfo &Info, CharUnits ObjectWidth, + const CastExpr *BCE) + : Info(Info), + Buffer(ObjectWidth, Info.Ctx.getTargetInfo().isLittleEndian()), + BCE(BCE) {} + + bool visit(const APValue &Val, QualType Ty) { + return visit(Val, Ty, CharUnits::fromQuantity(0)); + } + + // Write out Val with type Ty into Buffer starting at Offset. + bool visit(const APValue &Val, QualType Ty, CharUnits Offset) { + assert((size_t)Offset.getQuantity() <= Buffer.size()); + + // As a special case, nullptr_t has an indeterminate value. + if (Ty->isNullPtrType()) + return true; + + // Dig through Src to find the byte at SrcOffset. + switch (Val.getKind()) { + case APValue::Indeterminate: + case APValue::None: + return true; + + case APValue::Int: + return visitInt(Val.getInt(), Ty, Offset); + case APValue::Float: + return visitFloat(Val.getFloat(), Ty, Offset); + case APValue::Array: + return visitArray(Val, Ty, Offset); + case APValue::Struct: + return visitRecord(Val, Ty, Offset); + + case APValue::ComplexInt: + case APValue::ComplexFloat: + case APValue::Vector: + case APValue::FixedPoint: + // FIXME: We should support these. + + case APValue::Union: + case APValue::MemberPointer: + case APValue::AddrLabelDiff: { + Info.FFDiag(BCE->getBeginLoc(), + diag::note_constexpr_bit_cast_unsupported_type) + << Ty; + return false; + } + + case APValue::LValue: + llvm_unreachable("LValue subobject in bit_cast?"); + } + } + + bool visitRecord(const APValue &Val, QualType Ty, CharUnits Offset) { + const RecordDecl *RD = Ty->getAsRecordDecl(); + const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); + + // Visit the base classes. + if (auto *CXXRD = dyn_cast(RD)) { + for (size_t I = 0, E = CXXRD->getNumBases(); I != E; ++I) { + const CXXBaseSpecifier &BS = CXXRD->bases_begin()[I]; + CXXRecordDecl *BaseDecl = BS.getType()->getAsCXXRecordDecl(); + + if (!visitRecord(Val.getStructBase(I), BS.getType(), + Layout.getBaseClassOffset(BaseDecl) + Offset)) + return false; + } + } + + // 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; + } + + uint64_t FieldOffsetBits = Layout.getFieldOffset(FieldIdx); + + assert(FieldOffsetBits % Info.Ctx.getCharWidth() == 0 && + "only bit-fields can have sub-char alignment"); + CharUnits FieldOffset = + Info.Ctx.toCharUnitsFromBits(FieldOffsetBits) + Offset; + QualType FieldTy = FD->getType(); + if (!visit(Val.getStructField(FieldIdx), FieldTy, FieldOffset)) + return false; + ++FieldIdx; + } + + return true; + } + + bool visitArray(const APValue &Val, QualType Ty, CharUnits Offset) { + const auto *CAT = + dyn_cast_or_null(Ty->getAsArrayTypeUnsafe()); + if (!CAT) + return false; + + CharUnits ElemWidth = Info.Ctx.getTypeSizeInChars(CAT->getElementType()); + unsigned NumInitializedElts = Val.getArrayInitializedElts(); + unsigned ArraySize = Val.getArraySize(); + // First, initialize the initialized elements. + for (unsigned I = 0; I != NumInitializedElts; ++I) { + const APValue &SubObj = Val.getArrayInitializedElt(I); + if (!visit(SubObj, CAT->getElementType(), Offset + I * ElemWidth)) + return false; + } + + // Next, initialize the rest of the array using the filler. + if (Val.hasArrayFiller()) { + const APValue &Filler = Val.getArrayFiller(); + for (unsigned I = NumInitializedElts; I != ArraySize; ++I) { + if (!visit(Filler, CAT->getElementType(), Offset + I * ElemWidth)) + return false; + } + } + + return true; + } + + bool visitInt(const APSInt &Val, QualType Ty, CharUnits Offset) { + CharUnits Width = Info.Ctx.getTypeSizeInChars(Ty); + SmallVector Bytes(Width.getQuantity()); + llvm::StoreIntToMemory(Val, &*Bytes.begin(), Width.getQuantity()); + Buffer.writeObject(Offset, Bytes); + return true; + } + + bool visitFloat(const APFloat &Val, QualType Ty, CharUnits Offset) { + APSInt AsInt(Val.bitcastToAPInt()); + return visitInt(AsInt, Ty, Offset); + } + +public: + static Optional convert(EvalInfo &Info, const APValue &Src, + const CastExpr *BCE) { + CharUnits DstSize = Info.Ctx.getTypeSizeInChars(BCE->getType()); + APValueToBufferConverter Converter(Info, DstSize, BCE); + if (!Converter.visit(Src, BCE->getSubExpr()->getType())) + return None; + return Converter.Buffer; + } +}; + +/// Write an BitCastBuffer into an APValue. +class BufferToAPValueConverter { + EvalInfo &Info; + const BitCastBuffer &Buffer; + const CastExpr *BCE; + + BufferToAPValueConverter(EvalInfo &Info, const BitCastBuffer &Buffer, + const CastExpr *BCE) + : Info(Info), Buffer(Buffer), BCE(BCE) {} + + // Emit an unsupported bit_cast type error. Sema refuses to build a bit_cast + // with an invalid type, so anything left is a deficiency on our part (FIXME). + // Ideally this will be unreachable. + llvm::NoneType unsupportedType(QualType Ty) { + Info.FFDiag(BCE->getBeginLoc(), + diag::note_constexpr_bit_cast_unsupported_type) + << Ty; + return None; + } + + Optional visit(const BuiltinType *T, CharUnits Offset, + const EnumType *EnumSugar = nullptr) { + if (T->isNullPtrType()) { + uint64_t NullValue = Info.Ctx.getTargetNullPointerValue(QualType(T, 0)); + return APValue((Expr *)nullptr, + /*Offset=*/CharUnits::fromQuantity(NullValue), + APValue::NoLValuePath{}, /*IsNullPtr=*/true); + } + + CharUnits SizeOf = Info.Ctx.getTypeSizeInChars(T); + SmallVector Bytes; + if (!Buffer.readObject(Offset, SizeOf, Bytes)) { + // 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 ? (Type *)EnumSugar : T, 0); + Info.FFDiag(BCE->getExprLoc(), + diag::note_constexpr_bit_cast_indet_dest) + << DisplayType << Info.Ctx.getLangOpts().CharIsSigned; + return None; + } + + return APValue::IndeterminateValue(); + } + + APSInt Val(SizeOf.getQuantity() * Info.Ctx.getCharWidth(), true); + llvm::LoadIntFromMemory(Val, &*Bytes.begin(), Bytes.size()); + + if (T->isIntegralOrEnumerationType()) { + Val.setIsSigned(T->isSignedIntegerOrEnumerationType()); + return APValue(Val); + } + + if (T->isRealFloatingType()) { + const llvm::fltSemantics &Semantics = + Info.Ctx.getFloatTypeSemantics(QualType(T, 0)); + return APValue(APFloat(Semantics, Val)); + } + + return unsupportedType(QualType(T, 0)); + } + + Optional visit(const RecordType *RTy, CharUnits Offset) { + const RecordDecl *RD = RTy->getAsRecordDecl(); + const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); + + unsigned NumBases = 0; + if (auto *CXXRD = dyn_cast(RD)) + NumBases = CXXRD->getNumBases(); + + APValue ResultVal(APValue::UninitStruct(), NumBases, + std::distance(RD->field_begin(), RD->field_end())); + + // Visit the base classes. + if (auto *CXXRD = dyn_cast(RD)) { + for (size_t I = 0, E = CXXRD->getNumBases(); I != E; ++I) { + const CXXBaseSpecifier &BS = CXXRD->bases_begin()[I]; + CXXRecordDecl *BaseDecl = BS.getType()->getAsCXXRecordDecl(); + if (BaseDecl->isEmpty() || + Info.Ctx.getASTRecordLayout(BaseDecl).getNonVirtualSize().isZero()) + continue; + + Optional SubObj = visitType( + BS.getType(), Layout.getBaseClassOffset(BaseDecl) + Offset); + if (!SubObj) + return None; + ResultVal.getStructBase(I) = *SubObj; + } + } + + // 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 None; + } + + uint64_t FieldOffsetBits = Layout.getFieldOffset(FieldIdx); + assert(FieldOffsetBits % Info.Ctx.getCharWidth() == 0); + + CharUnits FieldOffset = + CharUnits::fromQuantity(FieldOffsetBits / Info.Ctx.getCharWidth()) + + Offset; + QualType FieldTy = FD->getType(); + Optional SubObj = visitType(FieldTy, FieldOffset); + if (!SubObj) + return None; + ResultVal.getStructField(FieldIdx) = *SubObj; + ++FieldIdx; + } + + return ResultVal; + } + + Optional visit(const EnumType *Ty, CharUnits Offset) { + QualType RepresentationType = Ty->getDecl()->getIntegerType(); + assert(!RepresentationType.isNull() && + "enum forward decl should be caught by Sema"); + const BuiltinType *AsBuiltin = + RepresentationType.getCanonicalType()->getAs(); + assert(AsBuiltin && "non-integral enum underlying type?"); + // Recurse into the underlying type. Treat std::byte transparently as + // unsigned char. + return visit(AsBuiltin, Offset, /*EnumTy=*/Ty); + } + + Optional visit(const ConstantArrayType *Ty, CharUnits Offset) { + size_t Size = Ty->getSize().getLimitedValue(); + CharUnits ElementWidth = Info.Ctx.getTypeSizeInChars(Ty->getElementType()); + + APValue ArrayValue(APValue::UninitArray(), Size, Size); + for (size_t I = 0; I != Size; ++I) { + Optional ElementValue = + visitType(Ty->getElementType(), Offset + I * ElementWidth); + if (!ElementValue) + return None; + ArrayValue.getArrayInitializedElt(I) = std::move(*ElementValue); + } + + return ArrayValue; + } + + Optional visit(const Type *Ty, CharUnits Offset) { + return unsupportedType(QualType(Ty, 0)); + } + + Optional visitType(QualType Ty, CharUnits Offset) { + QualType Can = Ty.getCanonicalType(); + + switch (Can->getTypeClass()) { +#define TYPE(Class, Base) \ + case Type::Class: \ + return visit(cast(Can.getTypePtr()), Offset); +#define ABSTRACT_TYPE(Class, Base) +#define NON_CANONICAL_TYPE(Class, Base) \ + case Type::Class: \ + llvm_unreachable("non-canonical type should be impossible!"); +#define DEPENDENT_TYPE(Class, Base) \ + case Type::Class: \ + llvm_unreachable( \ + "dependent types aren't supported in the constant evaluator!"); +#define NON_CANONICAL_UNLESS_DEPENDENT(Class, Base) \ + case Type::Class: \ + llvm_unreachable("either dependent or not canonical!"); +#include "clang/AST/TypeNodes.def" + } + } + +public: + // Pull out a full value of type DstType. + static Optional convert(EvalInfo &Info, BitCastBuffer &Buffer, + const CastExpr *BCE) { + BufferToAPValueConverter Converter(Info, Buffer, BCE); + return Converter.visitType(BCE->getType(), CharUnits::fromQuantity(0)); + } +}; + +static bool checkBitCastConstexprEligibilityType(SourceLocation Loc, + QualType Ty, EvalInfo *Info, + const ASTContext &Ctx, + bool CheckingDest) { + Ty = Ty.getCanonicalType(); + + auto diag = [&](int Reason) { + if (Info) + Info->FFDiag(Loc, diag::note_constexpr_bit_cast_invalid_type) + << CheckingDest << (Reason == 4) << Reason; + return false; + }; + auto note = [&](int Construct, QualType NoteTy, SourceLocation NoteLoc) { + if (Info) + Info->Note(NoteLoc, diag::note_constexpr_bit_cast_invalid_subtype) + << NoteTy << Construct << Ty; + return false; + }; + + if (Ty->isUnionType()) + return diag(0); + if (Ty->isPointerType()) + return diag(1); + if (Ty->isMemberPointerType()) + return diag(2); + if (Ty.isVolatileQualified()) + return diag(3); + + if (RecordDecl *Record = Ty->getAsRecordDecl()) { + if (auto *CXXRD = dyn_cast(Record)) { + for (CXXBaseSpecifier &BS : CXXRD->bases()) + if (!checkBitCastConstexprEligibilityType(Loc, BS.getType(), Info, Ctx, + CheckingDest)) + return note(1, BS.getType(), BS.getBeginLoc()); + } + for (FieldDecl *FD : Record->fields()) { + if (FD->getType()->isReferenceType()) + return diag(4); + if (!checkBitCastConstexprEligibilityType(Loc, FD->getType(), Info, Ctx, + CheckingDest)) + return note(0, FD->getType(), FD->getBeginLoc()); + } + } + + if (Ty->isArrayType() && + !checkBitCastConstexprEligibilityType(Loc, Ctx.getBaseElementType(Ty), + Info, Ctx, CheckingDest)) + return false; + + return true; +} + +static bool checkBitCastConstexprEligibility(EvalInfo *Info, + const ASTContext &Ctx, + const CastExpr *BCE) { + bool DestOK = checkBitCastConstexprEligibilityType( + BCE->getBeginLoc(), BCE->getType(), Info, Ctx, true); + bool SourceOK = DestOK && checkBitCastConstexprEligibilityType( + BCE->getBeginLoc(), + BCE->getSubExpr()->getType(), Info, Ctx, false); + return SourceOK; +} + +static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, + APValue &SourceValue, + const CastExpr *BCE) { + assert(CHAR_BIT == 8 && Info.Ctx.getTargetInfo().getCharWidth() == 8 && + "no host or target supports non 8-bit chars"); + assert(SourceValue.isLValue() && + "LValueToRValueBitcast requires an lvalue operand!"); + + if (!checkBitCastConstexprEligibility(&Info, Info.Ctx, BCE)) + return false; + + LValue SourceLValue; + APValue SourceRValue; + SourceLValue.setFrom(Info.Ctx, SourceValue); + if (!handleLValueToRValueConversion(Info, BCE, + BCE->getSubExpr()->getType().withConst(), + SourceLValue, SourceRValue)) + return false; + + // Read out SourceValue into a char buffer. + Optional Buffer = + APValueToBufferConverter::convert(Info, SourceRValue, BCE); + if (!Buffer) + return false; + + // Write out the buffer into a new APValue. + Optional MaybeDestValue = + BufferToAPValueConverter::convert(Info, *Buffer, BCE); + if (!MaybeDestValue) + return false; + + DestValue = std::move(*MaybeDestValue); + return true; +} + template class ExprEvaluatorBase : public ConstStmtVisitor { @@ -5510,6 +5992,9 @@ public: CCEDiag(E, diag::note_constexpr_invalid_cast) << 1; return static_cast(this)->VisitCastExpr(E); } + bool VisitBuiltinBitCastExpr(const BuiltinBitCastExpr *E) { + return static_cast(this)->VisitCastExpr(E); + } bool VisitBinaryOperator(const BinaryOperator *E) { switch (E->getOpcode()) { @@ -5802,6 +6287,14 @@ public: return false; return DerivedSuccess(RVal, E); } + case CK_LValueToRValueBitCast: { + APValue DestValue, SourceValue; + if (!Evaluate(SourceValue, Info, E->getSubExpr())) + return false; + if (!handleLValueToRValueBitCast(Info, DestValue, SourceValue, E)) + return false; + return DerivedSuccess(DestValue, E); + } } return Error(E); @@ -6651,7 +7144,6 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { switch (E->getCastKind()) { default: break; - case CK_BitCast: case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: @@ -8176,7 +8668,7 @@ public: } bool Success(const APValue &V, const Expr *E) { - if (V.isLValue() || V.isAddrLabelDiff()) { + if (V.isLValue() || V.isAddrLabelDiff() || V.isIndeterminate()) { Result = V; return true; } @@ -10598,6 +11090,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_LValueToRValue: case CK_AtomicToNonAtomic: case CK_NoOp: + case CK_LValueToRValueBitCast: return ExprEvaluatorBaseTy::VisitCastExpr(E); case CK_MemberPointerToBoolean: @@ -11210,6 +11703,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_LValueToRValue: case CK_AtomicToNonAtomic: case CK_NoOp: + case CK_LValueToRValueBitCast: return ExprEvaluatorBaseTy::VisitCastExpr(E); case CK_Dependent: @@ -12512,6 +13006,11 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::ChooseExprClass: { return CheckICE(cast(E)->getChosenSubExpr(), Ctx); } + case Expr::BuiltinBitCastExprClass: { + if (!checkBitCastConstexprEligibility(nullptr, Ctx, cast(E))) + return ICEDiag(IK_NotICE, E->getBeginLoc()); + return CheckICE(cast(E)->getSubExpr(), Ctx); + } } llvm_unreachable("Invalid StmtClass!"); diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 79be51cd9c..99556a6e70 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -3634,6 +3634,7 @@ recurse: case Expr::AtomicExprClass: case Expr::SourceLocExprClass: case Expr::FixedPointLiteralClass: + case Expr::BuiltinBitCastExprClass: { if (!NullOut) { // As bad as this diagnostic is, it's better than crashing. diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 83c017fdc8..46802d765e 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1703,6 +1703,14 @@ void StmtPrinter::VisitCXXConstCastExpr(CXXConstCastExpr *Node) { VisitCXXNamedCastExpr(Node); } +void StmtPrinter::VisitBuiltinBitCastExpr(BuiltinBitCastExpr *Node) { + OS << "__builtin_bit_cast("; + Node->getTypeInfoAsWritten()->getType().print(OS, Policy); + OS << ", "; + PrintExpr(Node->getSubExpr()); + OS << ")"; +} + void StmtPrinter::VisitCXXTypeidExpr(CXXTypeidExpr *Node) { OS << "typeid("; if (Node->isTypeOperand()) { diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index c5da5bfda9..f92c3dc60b 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -1569,6 +1569,11 @@ void StmtProfiler::VisitCXXConstCastExpr(const CXXConstCastExpr *S) { VisitCXXNamedCastExpr(S); } +void StmtProfiler::VisitBuiltinBitCastExpr(const BuiltinBitCastExpr *S) { + VisitExpr(S); + VisitType(S->getTypeInfoAsWritten()->getType()); +} + void StmtProfiler::VisitUserDefinedLiteral(const UserDefinedLiteral *S) { VisitCallExpr(S); } diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index abe10d383d..4e97fba262 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -4209,6 +4209,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { switch (E->getCastKind()) { case CK_ToVoid: case CK_BitCast: + case CK_LValueToRValueBitCast: case CK_ArrayToPointerDecay: case CK_FunctionToPointerDecay: case CK_NullToMemberPointer: diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index fe31f83628..695facd50b 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -711,6 +711,25 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { break; } + case CK_LValueToRValueBitCast: { + if (Dest.isIgnored()) { + CGF.EmitAnyExpr(E->getSubExpr(), AggValueSlot::ignored(), + /*ignoreResult=*/true); + break; + } + + LValue SourceLV = CGF.EmitLValue(E->getSubExpr()); + Address SourceAddress = + Builder.CreateElementBitCast(SourceLV.getAddress(), CGF.Int8Ty); + Address DestAddress = + Builder.CreateElementBitCast(Dest.getAddress(), CGF.Int8Ty); + llvm::Value *SizeVal = llvm::ConstantInt::get( + CGF.SizeTy, + CGF.getContext().getTypeSizeInChars(E->getType()).getQuantity()); + Builder.CreateMemCpy(DestAddress, SourceAddress, SizeVal); + break; + } + case CK_DerivedToBase: case CK_BaseToDerived: case CK_UncheckedDerivedToBase: { diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp index 4906b63292..6a5fb45ba2 100644 --- a/lib/CodeGen/CGExprComplex.cpp +++ b/lib/CodeGen/CGExprComplex.cpp @@ -464,6 +464,15 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op, return EmitLoadOfLValue(CGF.MakeAddrLValue(V, DestTy), Op->getExprLoc()); } + case CK_LValueToRValueBitCast: { + LValue SourceLVal = CGF.EmitLValue(Op); + Address Addr = Builder.CreateElementBitCast(SourceLVal.getAddress(), + CGF.ConvertTypeForMem(DestTy)); + LValue DestLV = CGF.MakeAddrLValue(Addr, DestTy); + DestLV.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo()); + return EmitLoadOfLValue(DestLV, Op->getExprLoc()); + } + case CK_BitCast: case CK_BaseToDerived: case CK_DerivedToBase: diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index 4a0494295d..9f4b686ad9 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -1115,6 +1115,7 @@ public: case CK_ToVoid: case CK_Dynamic: case CK_LValueBitCast: + case CK_LValueToRValueBitCast: case CK_NullToMemberPointer: case CK_UserDefinedConversion: case CK_CPointerToObjCPointerCast: diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 5e979c0de9..ef6b84737b 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -2033,6 +2033,15 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { return EmitLoadOfLValue(LV, CE->getExprLoc()); } + case CK_LValueToRValueBitCast: { + LValue SourceLVal = CGF.EmitLValue(E); + Address Addr = Builder.CreateElementBitCast(SourceLVal.getAddress(), + CGF.ConvertTypeForMem(DestTy)); + LValue DestLV = CGF.MakeAddrLValue(Addr, DestTy); + DestLV.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo()); + return EmitLoadOfLValue(DestLV, CE->getExprLoc()); + } + case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: diff --git a/lib/Edit/RewriteObjCFoundationAPI.cpp b/lib/Edit/RewriteObjCFoundationAPI.cpp index 4dfe170ed6..6f4a880b64 100644 --- a/lib/Edit/RewriteObjCFoundationAPI.cpp +++ b/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1042,6 +1042,7 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, case CK_Dependent: case CK_BitCast: case CK_LValueBitCast: + case CK_LValueToRValueBitCast: case CK_BaseToDerived: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 7e1f3b4dfd..5021ff8aac 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1630,6 +1630,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { .Case("__builtin_FILE", true) .Case("__builtin_FUNCTION", true) .Case("__builtin_COLUMN", true) + .Case("__builtin_bit_cast", true) .Default(false); } }); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index dbea774118..b5c01fe60f 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1223,6 +1223,9 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::kw_static_cast: Res = ParseCXXCasts(); break; + case tok::kw___builtin_bit_cast: + Res = ParseBuiltinBitCast(); + break; case tok::kw_typeid: Res = ParseCXXTypeid(); break; diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 222c118262..9d296f1852 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -3513,3 +3513,37 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, ConsumeAnyToken(); return Result; } + +/// Parse a __builtin_bit_cast(T, E). +ExprResult Parser::ParseBuiltinBitCast() { + SourceLocation KWLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, "__builtin_bit_cast")) + return ExprError(); + + // Parse the common declaration-specifiers piece. + DeclSpec DS(AttrFactory); + ParseSpecifierQualifierList(DS); + + // Parse the abstract-declarator, if present. + Declarator DeclaratorInfo(DS, DeclaratorContext::TypeNameContext); + ParseDeclarator(DeclaratorInfo); + + if (ExpectAndConsume(tok::comma)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::comma; + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + ExprResult Operand = ParseExpression(); + + if (T.consumeClose()) + return ExprError(); + + if (Operand.isInvalid() || DeclaratorInfo.isInvalidType()) + return ExprError(); + + return Actions.ActOnBuiltinBitCastExpr(KWLoc, DeclaratorInfo, Operand, + T.getCloseLocation()); +} diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index 46d0f57dc7..9ca6e4ab0e 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -87,6 +87,7 @@ namespace { void CheckDynamicCast(); void CheckCXXCStyleCast(bool FunctionalCast, bool ListInitialization); void CheckCStyleCast(); + void CheckBuiltinBitCast(); void updatePartOfExplicitCastFlags(CastExpr *CE) { // Walk down from the CE to the OrigSrcExpr, and mark all immediate @@ -331,6 +332,38 @@ Sema::BuildCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind, } } +ExprResult Sema::ActOnBuiltinBitCastExpr(SourceLocation KWLoc, Declarator &D, + ExprResult Operand, + SourceLocation RParenLoc) { + assert(!D.isInvalidType()); + + TypeSourceInfo *TInfo = GetTypeForDeclaratorCast(D, Operand.get()->getType()); + if (D.isInvalidType()) + return ExprError(); + + return BuildBuiltinBitCastExpr(KWLoc, TInfo, Operand.get(), RParenLoc); +} + +ExprResult Sema::BuildBuiltinBitCastExpr(SourceLocation KWLoc, + TypeSourceInfo *TSI, Expr *Operand, + SourceLocation RParenLoc) { + CastOperation Op(*this, TSI->getType(), Operand); + Op.OpRange = SourceRange(KWLoc, RParenLoc); + TypeLoc TL = TSI->getTypeLoc(); + Op.DestRange = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); + + if (!Operand->isTypeDependent() && !TSI->getType()->isDependentType()) { + Op.CheckBuiltinBitCast(); + if (Op.SrcExpr.isInvalid()) + return ExprError(); + } + + BuiltinBitCastExpr *BCE = + new (Context) BuiltinBitCastExpr(Op.ResultType, Op.ValueKind, Op.Kind, + Op.SrcExpr.get(), TSI, KWLoc, RParenLoc); + return Op.complete(BCE); +} + /// Try to diagnose a failed overloaded cast. Returns true if /// diagnostics were emitted. static bool tryDiagnoseOverloadedCast(Sema &S, CastType CT, @@ -2764,6 +2797,43 @@ void CastOperation::CheckCStyleCast() { checkCastAlign(); } +void CastOperation::CheckBuiltinBitCast() { + QualType SrcType = SrcExpr.get()->getType(); + if (SrcExpr.get()->isRValue()) + SrcExpr = Self.CreateMaterializeTemporaryExpr(SrcType, SrcExpr.get(), + /*IsLValueReference=*/false); + + CharUnits DestSize = Self.Context.getTypeSizeInChars(DestType); + CharUnits SourceSize = Self.Context.getTypeSizeInChars(SrcType); + if (DestSize != SourceSize) { + Self.Diag(OpRange.getBegin(), diag::err_bit_cast_type_size_mismatch) + << (int)SourceSize.getQuantity() << (int)DestSize.getQuantity(); + SrcExpr = ExprError(); + return; + } + + if (!DestType.isTriviallyCopyableType(Self.Context)) { + Self.Diag(OpRange.getBegin(), diag::err_bit_cast_non_trivially_copyable) + << 1; + SrcExpr = ExprError(); + return; + } + + if (!SrcType.isTriviallyCopyableType(Self.Context)) { + Self.Diag(OpRange.getBegin(), diag::err_bit_cast_non_trivially_copyable) + << 0; + SrcExpr = ExprError(); + return; + } + + if (Self.Context.hasSameUnqualifiedType(DestType, SrcType)) { + Kind = CK_NoOp; + return; + } + + Kind = CK_LValueToRValueBitCast; +} + /// DiagnoseCastQual - Warn whenever casts discards a qualifiers, be it either /// const, volatile or both. static void DiagnoseCastQual(Sema &Self, const ExprResult &SrcExpr, diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 5274532b0f..9fd924a8ca 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -1201,6 +1201,7 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::CoyieldExprClass: case Expr::CXXConstCastExprClass: case Expr::CXXReinterpretCastExprClass: + case Expr::BuiltinBitCastExprClass: case Expr::CXXStdInitializerListExprClass: case Expr::DesignatedInitExprClass: case Expr::DesignatedInitUpdateExprClass: diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 953889c6a0..7edb2a8d80 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -2652,6 +2652,16 @@ public: ListInitialization); } + /// Build a new C++ __builtin_bit_cast expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildBuiltinBitCastExpr(SourceLocation KWLoc, + TypeSourceInfo *TSI, Expr *Sub, + SourceLocation RParenLoc) { + return getSema().BuildBuiltinBitCastExpr(KWLoc, TSI, Sub, RParenLoc); + } + /// Build a new C++ typeid(type) expression. /// /// By default, performs semantic analysis to build the new expression. @@ -10245,6 +10255,22 @@ TreeTransform::TransformCXXNamedCastExpr(CXXNamedCastExpr *E) { E->getAngleBrackets().getEnd(), SubExpr.get(), E->getRParenLoc()); } +template +ExprResult +TreeTransform::TransformBuiltinBitCastExpr(BuiltinBitCastExpr *BCE) { + TypeSourceInfo *TSI = + getDerived().TransformType(BCE->getTypeInfoAsWritten()); + if (!TSI) + return ExprError(); + + ExprResult Sub = getDerived().TransformExpr(BCE->getSubExpr()); + if (Sub.isInvalid()) + return ExprError(); + + return getDerived().RebuildBuiltinBitCastExpr(BCE->getBeginLoc(), TSI, + Sub.get(), BCE->getEndLoc()); +} + template ExprResult TreeTransform::TransformCXXStaticCastExpr(CXXStaticCastExpr *E) { diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 82f055c3ec..58103eaf9a 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -1510,6 +1510,12 @@ void ASTStmtReader::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E) { E->setRParenLoc(ReadSourceLocation()); } +void ASTStmtReader::VisitBuiltinBitCastExpr(BuiltinBitCastExpr *E) { + VisitExplicitCastExpr(E); + E->KWLoc = ReadSourceLocation(); + E->RParenLoc = ReadSourceLocation(); +} + void ASTStmtReader::VisitUserDefinedLiteral(UserDefinedLiteral *E) { VisitCallExpr(E); E->UDSuffixLoc = ReadSourceLocation(); diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 20a6b7d74c..486cc14e6b 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -1451,6 +1451,12 @@ void ASTStmtWriter::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E) { Code = serialization::EXPR_CXX_FUNCTIONAL_CAST; } +void ASTStmtWriter::VisitBuiltinBitCastExpr(BuiltinBitCastExpr *E) { + VisitExplicitCastExpr(E); + Record.AddSourceLocation(E->getBeginLoc()); + Record.AddSourceLocation(E->getEndLoc()); +} + void ASTStmtWriter::VisitUserDefinedLiteral(UserDefinedLiteral *E) { VisitCallExpr(E); Record.AddSourceLocation(E->UDSuffixLoc); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 628d4315bc..d95fb314f3 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1687,6 +1687,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXReinterpretCastExprClass: case Stmt::CXXConstCastExprClass: case Stmt::CXXFunctionalCastExprClass: + case Stmt::BuiltinBitCastExprClass: case Stmt::ObjCBridgedCastExprClass: { Bldr.takeNodes(Pred); const auto *C = cast(S); diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 3b5a437e3c..f436650fbd 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -380,6 +380,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: + case CK_LValueToRValueBitCast: case CK_AddressSpaceConversion: case CK_BooleanToSignedIntegral: case CK_IntegralToPointer: diff --git a/test/CodeGenCXX/builtin-bit-cast-no-tbaa.cpp b/test/CodeGenCXX/builtin-bit-cast-no-tbaa.cpp new file mode 100644 index 0000000000..3f0e490a5f --- /dev/null +++ b/test/CodeGenCXX/builtin-bit-cast-no-tbaa.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -O3 -std=c++2a -S -emit-llvm -o - -disable-llvm-passes -triple x86_64-apple-macos10.14 %s | FileCheck %s + +void test_scalar() { + // CHECK-LABEL: define void @_Z11test_scalarv + __builtin_bit_cast(float, 42); + + // CHECK: load float, float* {{.*}}, align 4, !tbaa ![[MAY_ALIAS_TBAA:.*]] +} + +void test_scalar2() { + // CHECK-LABEL: define void @_Z12test_scalar2v + struct S {int m;}; + __builtin_bit_cast(int, S{42}); + + // CHECK: load i32, i32* {{.*}}, align 4, !tbaa ![[MAY_ALIAS_TBAA]] +} + +// CHECK: ![[CHAR_TBAA:.*]] = !{!"omnipotent char", {{.*}}, i64 0} +// CHECK: ![[MAY_ALIAS_TBAA]] = !{![[CHAR_TBAA]], ![[CHAR_TBAA]], i64 0} diff --git a/test/CodeGenCXX/builtin-bit-cast.cpp b/test/CodeGenCXX/builtin-bit-cast.cpp new file mode 100644 index 0000000000..49f8ec3ad4 --- /dev/null +++ b/test/CodeGenCXX/builtin-bit-cast.cpp @@ -0,0 +1,106 @@ +// RUN: %clang_cc1 -std=c++2a -S -emit-llvm -o - -disable-llvm-passes -triple x86_64-apple-macos10.14 %s | FileCheck %s + +void test_scalar(int &oper) { + // CHECK-LABEL: define void @_Z11test_scalarRi + __builtin_bit_cast(float, oper); + + // CHECK: [[OPER:%.*]] = alloca i32* + // CHECK: [[REF:%.*]] = load i32*, i32** + // CHECK-NEXT: [[CASTED:%.*]] = bitcast i32* [[REF]] to float* + // CHECK-NEXT: load float, float* [[CASTED]] +} + +struct two_ints { + int x; + int y; +}; + +unsigned long test_aggregate_to_scalar(two_ints &ti) { + // CHECK-LABEL: define i64 @_Z24test_aggregate_to_scalarR8two_ints + return __builtin_bit_cast(unsigned long, ti); + + // CHECK: [[TI_ADDR:%.*]] = alloca %struct.two_ints*, align 8 + // CHECK: [[TI_LOAD:%.*]] = load %struct.two_ints*, %struct.two_ints** [[TI_ADDR]] + // CHECK-NEXT: [[CASTED:%.*]] = bitcast %struct.two_ints* [[TI_LOAD]] to i64* + // CHECK-NEXT: load i64, i64* [[CASTED]] +} + +struct two_floats { + float x; + float y; +}; + +two_floats test_aggregate_record(two_ints& ti) { + // CHECK-LABEL: define <2 x float> @_Z21test_aggregate_recordR8two_int + return __builtin_bit_cast(two_floats, ti); + + // CHECK: [[RETVAL:%.*]] = alloca %struct.two_floats, align 4 + // CHECK: [[TI:%.*]] = alloca %struct.two_ints*, align 8 + + // CHECK: [[LOAD_TI:%.*]] = load %struct.two_ints*, %struct.two_ints** [[TI]] + // CHECK: [[MEMCPY_SRC:%.*]] = bitcast %struct.two_ints* [[LOAD_TI]] to i8* + // CHECK: [[MEMCPY_DST:%.*]] = bitcast %struct.two_floats* [[RETVAL]] to i8* + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[MEMCPY_DST]], i8* align 4 [[MEMCPY_SRC]] +} + +two_floats test_aggregate_array(int (&ary)[2]) { + // CHECK-LABEL: define <2 x float> @_Z20test_aggregate_arrayRA2_i + return __builtin_bit_cast(two_floats, ary); + + // CHECK: [[RETVAL:%.*]] = alloca %struct.two_floats, align 4 + // CHECK: [[ARY:%.*]] = alloca [2 x i32]*, align 8 + + // CHECK: [[LOAD_ARY:%.*]] = load [2 x i32]*, [2 x i32]** [[ARY]] + // CHECK: [[MEMCPY_SRC:%.*]] = bitcast [2 x i32]* [[LOAD_ARY]] to i8* + // CHECK: [[MEMCPY_DST:%.*]] = bitcast %struct.two_floats* [[RETVAL]] to i8* + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[MEMCPY_DST]], i8* align 4 [[MEMCPY_SRC]] +} + +two_ints test_scalar_to_aggregate(unsigned long ul) { + // CHECK-LABEL: define i64 @_Z24test_scalar_to_aggregatem + return __builtin_bit_cast(two_ints, ul); + + // CHECK: [[TI:%.*]] = alloca %struct.two_ints, align 4 + // CHECK: [[TITMP:%.*]] = bitcast %struct.two_ints* [[TI]] to i8* + // CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[TITMP]] +} + +unsigned long test_complex(_Complex unsigned &cu) { + // CHECK-LABEL: define i64 @_Z12test_complexRCj + return __builtin_bit_cast(unsigned long, cu); + + // CHECK: [[REF_ALLOCA:%.*]] = alloca { i32, i32 }*, align 8 + // CHECK-NEXT: store { i32, i32 }* {{.*}}, { i32, i32 }** [[REF_ALLOCA]] + // CHECK-NEXT: [[REF:%.*]] = load { i32, i32 }*, { i32, i32 }** [[REF_ALLOCA]] + // CHECK-NEXT: [[CASTED:%.*]] = bitcast { i32, i32 }* [[REF]] to i64* + // CHECK-NEXT: load i64, i64* [[CASTED]], align 4 +} + +_Complex unsigned test_to_complex(unsigned long &ul) { + // CHECK-LABEL: define i64 @_Z15test_to_complexRm + + return __builtin_bit_cast(_Complex unsigned, ul); + + // CHECK: [[REF:%.*]] = alloca i64* + // CHECK: [[LOAD_REF:%.*]] = load i64*, i64** [[REF]] + // CHECK: [[CASTED:%.*]] = bitcast i64* [[LOAD_REF]] to { i32, i32 }* +} + +unsigned long test_array(int (&ary)[2]) { + // CHECK-LABEL: define i64 @_Z10test_arrayRA2_i + return __builtin_bit_cast(unsigned long, ary); + + // CHECK: [[REF_ALLOCA:%.*]] = alloca [2 x i32]* + // CHECK: [[LOAD_REF:%.*]] = load [2 x i32]*, [2 x i32]** [[REF_ALLOCA]] + // CHECK: [[CASTED:%.*]] = bitcast [2 x i32]* [[LOAD_REF]] to i64* + // CHECK: load i64, i64* [[CASTED]], align 4 +} + +two_ints test_rvalue_aggregate() { + // CHECK-LABEL: define i64 @_Z21test_rvalue_aggregate + return __builtin_bit_cast(two_ints, 42ul); + + // CHECK: [[TI:%.*]] = alloca %struct.two_ints, align 4 + // CHECK: [[CASTED:%.*]] = bitcast %struct.two_ints* [[TI]] to i8* + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 [[CASTED]] +} diff --git a/test/SemaCXX/builtin-bit-cast.cpp b/test/SemaCXX/builtin-bit-cast.cpp new file mode 100644 index 0000000000..3dddadc561 --- /dev/null +++ b/test/SemaCXX/builtin-bit-cast.cpp @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fno-signed-char + +#if !__has_builtin(__builtin_bit_cast) +#error +#endif + +template +T instantiate() { + return __builtin_bit_cast(T, v); +} + +int x = instantiate(); + +struct secret_ctor { + char member; + +private: secret_ctor() = default; +}; + +void test1() { + secret_ctor c = __builtin_bit_cast(secret_ctor, (char)0); +} + +void test2() { + constexpr int i = 0; + // expected-error@+1{{__builtin_bit_cast source size does not equal destination size (4 vs 1)}} + constexpr char c = __builtin_bit_cast(char, i); +} + +struct not_trivially_copyable { + virtual void foo() {} +}; + +// expected-error@+1{{__builtin_bit_cast source type must be trivially copyable}} +constexpr unsigned long ul = __builtin_bit_cast(unsigned long, not_trivially_copyable{}); + +// expected-error@+1 {{__builtin_bit_cast destination type must be trivially copyable}} +constexpr long us = __builtin_bit_cast(unsigned long &, 0L); diff --git a/test/SemaCXX/constexpr-builtin-bit-cast.cpp b/test/SemaCXX/constexpr-builtin-bit-cast.cpp new file mode 100644 index 0000000000..d48ee03dcc --- /dev/null +++ b/test/SemaCXX/constexpr-builtin-bit-cast.cpp @@ -0,0 +1,383 @@ +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple x86_64-apple-macosx10.14.0 %s -fno-signed-char +// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu %s + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define LITTLE_END 1 +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define LITTLE_END 0 +#else +# error "huh?" +#endif + +template struct is_same { + static constexpr bool value = false; +}; +template struct is_same { + static constexpr bool value = true; +}; + +static_assert(sizeof(int) == 4); +static_assert(sizeof(long long) == 8); + +template +constexpr To bit_cast(const From &from) { + static_assert(sizeof(To) == sizeof(From)); +#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) { + return bit_cast(bit_cast(init)) == 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)); +} + +void test_array() { + constexpr unsigned char input[] = {0xCA, 0xFE, 0xBA, 0xBE}; + constexpr unsigned expected = LITTLE_END ? 0xBEBAFECA : 0xCAFEBABE; + static_assert(bit_cast(input) == expected); +} + +void test_record() { + struct int_splicer { + unsigned x; + unsigned y; + + constexpr bool operator==(const int_splicer &other) const { + return other.x == x && other.y == y; + } + }; + + constexpr int_splicer splice{0x0C05FEFE, 0xCAFEBABE}; + + static_assert(bit_cast(splice) == LITTLE_END + ? 0xCAFEBABE0C05FEFE + : 0x0C05FEFECAFEBABE); + + static_assert(bit_cast(0xCAFEBABE0C05FEFE).x == LITTLE_END + ? 0x0C05FEFE + : 0xCAFEBABE); + + static_assert(round_trip(splice)); + static_assert(round_trip(splice)); + + struct base2 { + }; + + struct base3 { + unsigned z; + }; + + struct bases : int_splicer, base2, base3 { + unsigned doublez; + }; + + struct tuple4 { + unsigned x, y, z, doublez; + + constexpr bool operator==(tuple4 const &other) const { + return x == other.x && y == other.y && + z == other.z && doublez == other.doublez; + } + }; + 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)); +} + +void test_partially_initialized() { + struct pad { + signed char x; + int y; + }; + + struct no_pad { + signed char x; + signed char p1, p2, p3; + int y; + }; + + static_assert(sizeof(pad) == sizeof(no_pad)); + + 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-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); + + constexpr pad fine = bit_cast(no_pad{1, 2, 3, 4, 5}); + static_assert(fine.x == 1 && fine.y == 5); +} + +void no_bitfields() { + // FIXME! + struct S { + unsigned char x : 8; + }; + + struct G { + unsigned char x : 8; + }; + + 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); +} + +void array_members() { + struct S { + int ar[3]; + + constexpr bool operator==(const S &rhs) { + return ar[0] == rhs.ar[0] && ar[1] == rhs.ar[1] && ar[2] == rhs.ar[2]; + } + }; + + struct G { + int a, b, c; + + constexpr bool operator==(const G &rhs) { + return a == rhs.a && b == rhs.b && c == rhs.c; + } + }; + + constexpr S s{{1, 2, 3}}; + 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)); +} + +void bad_types() { + union X { + int x; + }; + + struct G { + int g; + }; + // expected-error@+2 {{constexpr variable 'g' must be initialized by a constant expression}} + // expected-note@+1 {{bit_cast from a union type is not allowed in a constant expression}} + constexpr G g = __builtin_bit_cast(G, X{0}); + // expected-error@+2 {{constexpr variable 'x' must be initialized by a constant expression}} + // expected-note@+1 {{bit_cast to a union type is not allowed in a constant expression}} + constexpr X x = __builtin_bit_cast(X, G{0}); + + struct has_pointer { + // expected-note@+1 2 {{invalid type 'int *' is a member of 'has_pointer'}} + int *ptr; + }; + + // expected-error@+2 {{constexpr variable 'ptr' must be initialized by a constant expression}} + // expected-note@+1 {{bit_cast from a pointer type is not allowed in a constant expression}} + constexpr unsigned long ptr = __builtin_bit_cast(unsigned long, has_pointer{0}); + // expected-error@+2 {{constexpr variable 'hptr' must be initialized by a constant expression}} + // expected-note@+1 {{bit_cast to a pointer type is not allowed in a constant expression}} + constexpr has_pointer hptr = __builtin_bit_cast(has_pointer, 0ul); +} + +void backtrace() { + struct A { + // expected-note@+1 {{invalid type 'int *' is a member of 'A'}} + int *ptr; + }; + + struct B { + // expected-note@+1 {{invalid type 'A [10]' is a member of 'B'}} + A as[10]; + }; + + // expected-note@+1 {{invalid type 'B' is a base of 'C'}} + struct C : B { + }; + + struct E { + unsigned long ar[10]; + }; + + // expected-error@+2 {{constexpr variable 'e' must be initialized by a constant expression}} + // expected-note@+1 {{bit_cast from a pointer type is not allowed in a constant expression}} + constexpr E e = __builtin_bit_cast(E, C{}); +} + +void test_array_fill() { + constexpr unsigned char a[4] = {1, 2}; + constexpr unsigned int i = bit_cast(a); + static_assert(i == LITTLE_END ? 0x00000201 : 0x01020000, ""); +} + +typedef decltype(nullptr) nullptr_t; + +#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 +// expected-note@+3 {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'unsigned long' is invalid}} +#endif +// expected-error@+1 {{constexpr variable 'test_from_nullptr' must be initialized by a constant expression}} +constexpr unsigned long test_from_nullptr = __builtin_bit_cast(unsigned long, nullptr); + +constexpr int test_from_nullptr_pass = (__builtin_bit_cast(unsigned char[8], nullptr), 0); + +constexpr int test_to_nullptr() { + nullptr_t npt = __builtin_bit_cast(nullptr_t, 0ul); + + struct indet_mem { + unsigned char data[sizeof(void *)]; + }; + indet_mem im = __builtin_bit_cast(indet_mem, nullptr); + nullptr_t npt2 = __builtin_bit_cast(nullptr_t, im); + + return 0; +} + +constexpr int ttn = test_to_nullptr(); + +// expected-warning@+2 {{returning reference to local temporary object}} +// expected-note@+1 {{temporary created here}} +constexpr const long &returns_local() { return 0L; } + +// expected-error@+2 {{constexpr variable 'test_nullptr_bad' must be initialized by a constant expression}} +// expected-note@+1 {{read of temporary whose lifetime has ended}} +constexpr nullptr_t test_nullptr_bad = __builtin_bit_cast(nullptr_t, returns_local()); + +constexpr int test_indeterminate(bool read_indet) { + struct pad { + char a; + int b; + }; + + struct no_pad { + char a; + unsigned char p1, p2, p3; + int b; + }; + + pad p{1, 2}; + no_pad np = bit_cast(p); + + int tmp = np.a + np.b; + + unsigned char& indet_ref = np.p1; + + if (read_indet) { + // expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}} + tmp = indet_ref; + } + + indet_ref = 0; + + return 0; +} + +constexpr int run_test_indeterminate = test_indeterminate(false); +// expected-error@+2 {{constexpr variable 'run_test_indeterminate2' must be initialized by a constant expression}} +// expected-note@+1 {{in call to 'test_indeterminate(true)'}} +constexpr int run_test_indeterminate2 = test_indeterminate(true); + +struct ref_mem { + const int &rm; +}; + +constexpr int global_int = 0; + +// expected-error@+2 {{constexpr variable 'run_ref_mem' must be initialized by a constant expression}} +// expected-note@+1 {{bit_cast from a type with a reference member is not allowed in a constant expression}} +constexpr unsigned long run_ref_mem = __builtin_bit_cast( + unsigned long, ref_mem{global_int}); + +union u { + int im; +}; + +// expected-error@+2 {{constexpr variable 'run_u' must be initialized by a constant expression}} +// expected-note@+1 {{bit_cast from a union type is not allowed in a constant expression}} +constexpr int run_u = __builtin_bit_cast(int, u{32}); + +struct vol_mem { + volatile int x; +}; + +// expected-error@+2 {{constexpr variable 'run_vol_mem' must be initialized by a constant expression}} +// expected-note@+1 {{non-literal type 'vol_mem' cannot be used in a constant expression}} +constexpr int run_vol_mem = __builtin_bit_cast(int, vol_mem{43}); + +struct mem_ptr { + int vol_mem::*x; // expected-note{{invalid type 'int vol_mem::*' is a member of 'mem_ptr'}} +}; +// expected-error@+2 {{constexpr variable 'run_mem_ptr' must be initialized by a constant expression}} +// expected-note@+1 {{bit_cast from a member pointer type is not allowed in a constant expression}} +constexpr int run_mem_ptr = __builtin_bit_cast(unsigned long, mem_ptr{nullptr}); + +struct A { char c; /* char padding : 8; */ short s; }; +struct B { unsigned char x[4]; }; + +constexpr B one() { + A a = {1, 2}; + return bit_cast(a); +} +constexpr char good_one = one().x[0] + one().x[2] + one().x[3]; +// expected-error@+2 {{constexpr variable 'bad_one' must be initialized by a constant expression}} +// expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}} +constexpr char bad_one = one().x[1]; + +constexpr A two() { + B b = one(); // b.x[1] is indeterminate. + b.x[0] = 'a'; + b.x[2] = 1; + b.x[3] = 2; + return bit_cast(b); +} +constexpr short good_two = two().c + two().s; + +namespace std { +enum byte : unsigned char {}; +} + +enum my_byte : unsigned char {}; + +struct pad { + char a; + int b; +}; + +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); + +#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}}}} +#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}} +#endif +constexpr int bad_char = (__builtin_bit_cast(char[8], pad{1, 2}), 0); + +struct pad_buffer { unsigned char data[sizeof(pad)]; }; +constexpr bool test_pad_buffer() { + pad x = {1, 2}; + pad_buffer y = __builtin_bit_cast(pad_buffer, x); + pad z = __builtin_bit_cast(pad, y); + return x.a == z.a && x.b == z.b; +} +static_assert(test_pad_buffer()); diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index c9d4c6f0a4..b3d2974b43 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -5194,6 +5194,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("CallExpr"); case CXCursor_ObjCMessageExpr: return cxstring::createRef("ObjCMessageExpr"); + case CXCursor_BuiltinBitCastExpr: + return cxstring::createRef("BuiltinBitCastExpr"); case CXCursor_UnexposedStmt: return cxstring::createRef("UnexposedStmt"); case CXCursor_DeclStmt: diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index 0d80cdb9af..422c2dacf7 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -716,6 +716,8 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: K = CXCursor_OMPTargetTeamsDistributeSimdDirective; break; + case Stmt::BuiltinBitCastExprClass: + K = CXCursor_BuiltinBitCastExpr; } CXCursor C = { K, 0, { Parent, S, TU } }; -- 2.40.0