Skip to content

Commit 2d50e77

Browse files
committed
[clang][bytecode] Activate primitive fields before initializing them
The initializer itself might need the field to be active.
1 parent 08399fd commit 2d50e77

File tree

5 files changed

+77
-42
lines changed

5 files changed

+77
-42
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -25,41 +25,6 @@ using APSInt = llvm::APSInt;
2525
namespace clang {
2626
namespace interp {
2727

28-
static bool hasTrivialDefaultCtorParent(const FieldDecl *FD) {
29-
assert(FD);
30-
assert(FD->getParent()->isUnion());
31-
const auto *CXXRD = dyn_cast<CXXRecordDecl>(FD->getParent());
32-
return !CXXRD || CXXRD->hasTrivialDefaultConstructor();
33-
}
34-
35-
static bool refersToUnion(const Expr *E) {
36-
for (;;) {
37-
if (const auto *ME = dyn_cast<MemberExpr>(E)) {
38-
if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
39-
FD && FD->getParent()->isUnion() && hasTrivialDefaultCtorParent(FD))
40-
return true;
41-
E = ME->getBase();
42-
continue;
43-
}
44-
45-
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
46-
E = ASE->getBase()->IgnoreImplicit();
47-
continue;
48-
}
49-
50-
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E);
51-
ICE && (ICE->getCastKind() == CK_NoOp ||
52-
ICE->getCastKind() == CK_DerivedToBase ||
53-
ICE->getCastKind() == CK_UncheckedDerivedToBase)) {
54-
E = ICE->getSubExpr();
55-
continue;
56-
}
57-
58-
break;
59-
}
60-
return false;
61-
}
62-
6328
static std::optional<bool> getBoolValue(const Expr *E) {
6429
if (const auto *CE = dyn_cast_if_present<ConstantExpr>(E);
6530
CE && CE->hasAPValueResult() &&
@@ -5409,6 +5374,53 @@ bool Compiler<Emitter>::maybeEmitDeferredVarInit(const VarDecl *VD) {
54095374
return true;
54105375
}
54115376

5377+
static bool hasTrivialDefaultCtorParent(const FieldDecl *FD) {
5378+
assert(FD);
5379+
assert(FD->getParent()->isUnion());
5380+
const auto *CXXRD = dyn_cast<CXXRecordDecl>(FD->getParent());
5381+
return !CXXRD || CXXRD->hasTrivialDefaultConstructor();
5382+
}
5383+
5384+
template <class Emitter> bool Compiler<Emitter>::refersToUnion(const Expr *E) {
5385+
for (;;) {
5386+
if (const auto *ME = dyn_cast<MemberExpr>(E)) {
5387+
if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
5388+
FD && FD->getParent()->isUnion() && hasTrivialDefaultCtorParent(FD))
5389+
return true;
5390+
E = ME->getBase();
5391+
continue;
5392+
}
5393+
5394+
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
5395+
E = ASE->getBase()->IgnoreImplicit();
5396+
continue;
5397+
}
5398+
5399+
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E);
5400+
ICE && (ICE->getCastKind() == CK_NoOp ||
5401+
ICE->getCastKind() == CK_DerivedToBase ||
5402+
ICE->getCastKind() == CK_UncheckedDerivedToBase)) {
5403+
E = ICE->getSubExpr();
5404+
continue;
5405+
}
5406+
5407+
if (const auto *This = dyn_cast<CXXThisExpr>(E)) {
5408+
const auto *ThisRecord =
5409+
This->getType()->getPointeeType()->getAsRecordDecl();
5410+
if (!ThisRecord->isUnion())
5411+
return false;
5412+
// Otherwise, always activate if we're in the ctor.
5413+
if (const auto *Ctor =
5414+
dyn_cast_if_present<CXXConstructorDecl>(CompilingFunction))
5415+
return Ctor->getParent() == ThisRecord;
5416+
return false;
5417+
}
5418+
5419+
break;
5420+
}
5421+
return false;
5422+
}
5423+
54125424
template <class Emitter>
54135425
bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
54145426
bool EvaluateConditionDecl) {
@@ -5941,16 +5953,15 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
59415953
return false;
59425954

59435955
if (std::optional<PrimType> T = this->classify(InitExpr)) {
5956+
if (Activate && !this->emitActivateThisField(FieldOffset, InitExpr))
5957+
return false;
5958+
59445959
if (!this->visit(InitExpr))
59455960
return false;
59465961

59475962
bool BitField = F->isBitField();
5948-
if (BitField && Activate)
5949-
return this->emitInitThisBitFieldActivate(*T, F, FieldOffset, InitExpr);
59505963
if (BitField)
59515964
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
5952-
if (Activate)
5953-
return this->emitInitThisFieldActivate(*T, FieldOffset, InitExpr);
59545965
return this->emitInitThisField(*T, FieldOffset, InitExpr);
59555966
}
59565967
// Non-primitive case. Get a pointer to the field-to-initialize

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
405405
bool checkLiteralType(const Expr *E);
406406
bool maybeEmitDeferredVarInit(const VarDecl *VD);
407407

408+
bool refersToUnion(const Expr *E);
409+
408410
protected:
409411
/// Variable to storage mapping.
410412
llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,16 @@ static inline bool Activate(InterpState &S, CodePtr OpPC) {
19831983
return true;
19841984
}
19851985

1986+
static inline bool ActivateThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
1987+
if (S.checkingPotentialConstantExpression())
1988+
return false;
1989+
1990+
const Pointer &Ptr = S.Current->getThis();
1991+
assert(Ptr.atField(I).canBeInitialized());
1992+
Ptr.atField(I).activate();
1993+
return true;
1994+
}
1995+
19861996
template <PrimType Name, class T = typename PrimConv<Name>::T>
19871997
bool StoreActivate(InterpState &S, CodePtr OpPC) {
19881998
const T &Value = S.Stk.pop<T>();

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ def StoreBitFieldActivate : StoreBitFieldOpcode {}
510510
def StoreBitFieldActivatePop : StoreBitFieldOpcode {}
511511

512512
def Activate : Opcode {}
513+
def ActivateThisField : Opcode { let Args = [ArgUint32]; }
513514

514515
// [Pointer, Value] -> []
515516
def Init : StoreOpcode {}

clang/test/AST/ByteCode/unions.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,9 @@ namespace DefaultInit {
7979

8080
constexpr U1 u1; /// OK.
8181

82-
constexpr int foo() { // expected-error {{never produces a constant expression}}
82+
constexpr int foo() {
8383
U1 u;
84-
return u.a; // both-note {{read of member 'a' of union with active member 'b'}} \
85-
// expected-note {{read of member 'a' of union with active member 'b'}}
84+
return u.a; // both-note {{read of member 'a' of union with active member 'b'}}
8685
}
8786
static_assert(foo() == 42); // both-error {{not an integral constant expression}} \
8887
// both-note {{in call to}}
@@ -916,6 +915,18 @@ namespace NonTrivialCtor {
916915

917916
}
918917

918+
namespace PrimitiveFieldInitActivates {
919+
/// The initializer of a needs the field to be active _before_ it's visited.
920+
template<int> struct X {};
921+
union V {
922+
int a, b;
923+
constexpr V(X<0>) : a(a = 1) {} // ok
924+
constexpr V(X<2>) : a() { b = 1; } // ok
925+
};
926+
constinit V v0 = X<0>();
927+
constinit V v2 = X<2>();
928+
}
929+
919930
#endif
920931

921932
namespace AddressComparison {

0 commit comments

Comments
 (0)