From: Richard Smith Date: Tue, 6 Aug 2013 07:09:20 +0000 (+0000) Subject: PR16755: When initializing or modifying a bitfield member in a constant X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3835a4ef050da466038844274d79f1fc9d77c0f1;p=clang PR16755: When initializing or modifying a bitfield member in a constant expression, truncate the stored value to the size of the bitfield. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@187782 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 47b0a66900..fea78835b3 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1386,6 +1386,27 @@ static bool HandleIntToFloatCast(EvalInfo &Info, const Expr *E, return true; } +static bool truncateBitfieldValue(EvalInfo &Info, const Expr *E, + APValue &Value, const FieldDecl *FD) { + assert(FD->isBitField() && "truncateBitfieldValue on non-bitfield"); + + if (!Value.isInt()) { + // Trying to store a pointer-cast-to-integer into a bitfield. + // FIXME: In this case, we should provide the diagnostic for casting + // a pointer to an integer. + assert(Value.isLValue() && "integral value neither int nor lvalue?"); + Info.Diag(E); + return false; + } + + APSInt &Int = Value.getInt(); + unsigned OldBitWidth = Int.getBitWidth(); + unsigned NewBitWidth = FD->getBitWidthValue(Info.Ctx); + if (NewBitWidth < OldBitWidth) + Int = Int.trunc(NewBitWidth).extend(OldBitWidth); + return true; +} + static bool EvalAndBitcastToAPInt(EvalInfo &Info, const Expr *E, llvm::APInt &Res) { APValue SVal; @@ -1953,6 +1974,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, APValue *O = Obj.Value; QualType ObjType = Obj.Type; + const FieldDecl *LastField = 0; + // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) { if (O->isUninit()) { @@ -1961,9 +1984,20 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, return handler.failed(); } - if (I == N) - return handler.found(*O, ObjType); + if (I == N) { + if (!handler.found(*O, ObjType)) + return false; + + // If we modified a bit-field, truncate it to the right width. + if (handler.AccessKind != AK_Read && + LastField && LastField->isBitField() && + !truncateBitfieldValue(Info, E, *O, LastField)) + return false; + return true; + } + + LastField = 0; if (ObjType->isArrayType()) { // Next subobject is an array element. const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(ObjType); @@ -2065,6 +2099,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, } return handler.failed(); } + + LastField = Field; } else { // Next subobject is a base class. const CXXRecordDecl *Derived = ObjType->getAsCXXRecordDecl(); @@ -3522,6 +3558,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, APValue *Value = &Result; // Determine the subobject to initialize. + FieldDecl *FD = 0; if ((*I)->isBaseInitializer()) { QualType BaseType((*I)->getBaseClass(), 0); #ifndef NDEBUG @@ -3536,7 +3573,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, BaseType->getAsCXXRecordDecl(), &Layout)) return false; Value = &Result.getStructBase(BasesSeen++); - } else if (FieldDecl *FD = (*I)->getMember()) { + } else if ((FD = (*I)->getMember())) { if (!HandleLValueMember(Info, (*I)->getInit(), Subobject, FD, &Layout)) return false; if (RD->isUnion()) { @@ -3551,7 +3588,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, for (IndirectFieldDecl::chain_iterator C = IFD->chain_begin(), CE = IFD->chain_end(); C != CE; ++C) { - FieldDecl *FD = cast(*C); + FD = cast(*C); CXXRecordDecl *CD = cast(FD->getParent()); // Switch the union field if it differs. This happens if we had // preceding zero-initialization, and we're now initializing a union @@ -3578,7 +3615,9 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, } FullExpressionRAII InitScope(Info); - if (!EvaluateInPlace(*Value, Info, Subobject, (*I)->getInit())) { + if (!EvaluateInPlace(*Value, Info, Subobject, (*I)->getInit()) || + (FD && FD->isBitField() && !truncateBitfieldValue(Info, (*I)->getInit(), + *Value, FD))) { // If we're checking for a potential constant expression, evaluate all // initializers even if some of them fail. if (!Info.keepEvaluatingAfterFailure()) @@ -4918,8 +4957,10 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { ThisOverrideRAII ThisOverride(*Info.CurrentCall, &This, isa(Init)); - if (!EvaluateInPlace(Result.getStructField(Field->getFieldIndex()), Info, - Subobject, Init)) { + APValue &FieldVal = Result.getStructField(Field->getFieldIndex()); + if (!EvaluateInPlace(FieldVal, Info, Subobject, Init) || + (Field->isBitField() && !truncateBitfieldValue(Info, Init, + FieldVal, *Field))) { if (!Info.keepEvaluatingAfterFailure()) return false; Success = false; diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 8d16962387..6824909db8 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1727,3 +1727,40 @@ namespace Lifetime { constexpr int k1 = S().t; // ok, int is lifetime-extended to end of constructor constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}} } + +namespace Bitfields { + struct A { + bool b : 1; + unsigned u : 5; + int n : 5; + bool b2 : 3; + unsigned u2 : 74; // expected-warning {{exceeds the size of its type}} + int n2 : 81; // expected-warning {{exceeds the size of its type}} + }; + + constexpr A a = { false, 33, 31, false, 0xffffffff, 0x7fffffff }; // expected-warning 2{{truncation}} + static_assert(a.b == 0 && a.u == 1 && a.n == -1 && a.b2 == 0 && + a.u2 + 1 == 0 && a.n2 == 0x7fffffff, + "bad truncation of bitfield values"); + + struct B { + int n : 3; + constexpr B(int k) : n(k) {} + }; + static_assert(B(3).n == 3, ""); + static_assert(B(4).n == -4, ""); + static_assert(B(7).n == -1, ""); + static_assert(B(8).n == 0, ""); + static_assert(B(-1).n == -1, ""); + static_assert(B(-8889).n == -1, ""); + + namespace PR16755 { + struct X { + int x : 1; + constexpr static int f(int x) { + return X{x}.x; + } + }; + static_assert(X::f(3) == -1, "3 should truncate to -1"); + } +} diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index ee88a3b959..ebe8e989a8 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -870,3 +870,20 @@ namespace Lifetime { } static_assert((lifetime_versus_loops(), true), ""); } + +namespace Bitfields { + struct A { + bool b : 3; + int n : 4; + unsigned u : 5; + }; + constexpr bool test() { + A a {}; + a.b += 2; + --a.n; + --a.u; + a.n = -a.n * 3; + return a.b == false && a.n == 3 && a.u == 31; + } + static_assert(test(), ""); +}