forked from OSchip/llvm-project
PR16755: When initializing or modifying a bitfield member in a constant
expression, truncate the stored value to the size of the bitfield. llvm-svn: 187782
This commit is contained in:
parent
7418ff460c
commit
49ca8aab58
|
@ -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<FieldDecl>(*C);
|
||||
FD = cast<FieldDecl>(*C);
|
||||
CXXRecordDecl *CD = cast<CXXRecordDecl>(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<CXXDefaultInitExpr>(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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(), "");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue