diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 9bd040a208b3f..e7a431ddee6f0 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -342,8 +342,10 @@ bool ByteCodeExprGen::VisitBinaryOperator(const BinaryOperator *BO) { return Discard(this->emitDiv(*T, BO)); case BO_Assign: if (DiscardResult) - return this->emitStorePop(*T, BO); - return this->emitStore(*T, BO); + return LHS->refersToBitField() ? this->emitStoreBitFieldPop(*T, BO) + : this->emitStorePop(*T, BO); + return LHS->refersToBitField() ? this->emitStoreBitField(*T, BO) + : this->emitStore(*T, BO); case BO_And: return Discard(this->emitBitAnd(*T, BO)); case BO_Or: @@ -547,8 +549,15 @@ bool ByteCodeExprGen::visitInitList(ArrayRef Inits, const Record::Field *FieldToInit = R->getField(InitIndex); if (!this->visit(Init)) return false; - if (!this->emitInitField(*T, FieldToInit->Offset, E)) - return false; + + if (FieldToInit->isBitField()) { + if (!this->emitInitBitField(*T, FieldToInit, E)) + return false; + } else { + if (!this->emitInitField(*T, FieldToInit->Offset, E)) + return false; + } + if (!this->emitPopPtr(E)) return false; ++InitIndex; diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp index a81c82c38955e..6193a8d55a146 100644 --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -166,8 +166,13 @@ bool ByteCodeStmtGen::visitFunc(const FunctionDecl *F) { if (!this->visit(InitExpr)) return false; - if (!this->emitInitThisField(*T, F->Offset, InitExpr)) - return false; + if (F->isBitField()) { + if (!this->emitInitThisBitField(*T, F, InitExpr)) + return false; + } else { + if (!this->emitInitThisField(*T, F->Offset, InitExpr)) + return false; + } } else { // Non-primitive case. Get a pointer to the field-to-initialize // on the stack and call visitInitialzer() for it. diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 47dc1d08c9c4d..25ce93ac03d37 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1071,6 +1071,7 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) { template ::T> bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) { + assert(F->isBitField()); if (S.checkingPotentialConstantExpression()) return false; const Pointer &This = S.Current->getThis(); @@ -1112,8 +1113,9 @@ bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) { template ::T> bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) { + assert(F->isBitField()); const T &Value = S.Stk.pop(); - const Pointer &Field = S.Stk.pop().atField(F->Offset); + const Pointer &Field = S.Stk.peek().atField(F->Offset); Field.deref() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx())); Field.activate(); Field.initialize(); @@ -1334,11 +1336,10 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) { return false; if (!Ptr.isRoot()) Ptr.initialize(); - if (auto *FD = Ptr.getField()) { + if (const auto *FD = Ptr.getField()) Ptr.deref() = Value.truncate(FD->getBitWidthValue(S.getCtx())); - } else { + else Ptr.deref() = Value; - } return true; } @@ -1350,11 +1351,10 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) { return false; if (!Ptr.isRoot()) Ptr.initialize(); - if (auto *FD = Ptr.getField()) { + if (const auto *FD = Ptr.getField()) Ptr.deref() = Value.truncate(FD->getBitWidthValue(S.getCtx())); - } else { + else Ptr.deref() = Value; - } return true; } diff --git a/clang/lib/AST/Interp/Record.h b/clang/lib/AST/Interp/Record.h index b81070aa77e84..b0952af2d1ac6 100644 --- a/clang/lib/AST/Interp/Record.h +++ b/clang/lib/AST/Interp/Record.h @@ -29,6 +29,7 @@ class Record final { const FieldDecl *Decl; unsigned Offset; Descriptor *Desc; + bool isBitField() const { return Decl->isBitField(); } }; /// Describes a base class. diff --git a/clang/test/AST/Interp/bitfields.cpp b/clang/test/AST/Interp/bitfields.cpp new file mode 100644 index 0000000000000..e078704fce51f --- /dev/null +++ b/clang/test/AST/Interp/bitfields.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -Wno-bitfield-constant-conversion -verify %s +// RUN: %clang_cc1 -verify=ref -Wno-bitfield-constant-conversion %s + +// expected-no-diagnostics +// ref-no-diagnostics + +namespace Basic { + struct A { + unsigned int a : 2; + constexpr A() : a(0) {} + constexpr A(int a) : a(a) {} + }; + + constexpr A a{1}; + static_assert(a.a == 1, ""); + + constexpr A a2{10}; + static_assert(a2.a == 2, ""); + + + constexpr int storeA() { + A a; + a.a = 10; + + return a.a; + } + static_assert(storeA() == 2, ""); + + constexpr int storeA2() { + A a; + return a.a = 10; + } + static_assert(storeA2() == 2, ""); + + // TODO: +=, -=, etc. operators. +} + +namespace Overflow { + struct A {int c:3;}; + + constexpr int f() { + A a1{3}; + return a1.c++; + } + + static_assert(f() == 3, ""); +}