Skip to content

Commit 23eef9a

Browse files
authored
[clang][bytecode] Activate primitive fields before initializing them (#149963)
The initializer itself might need the field to be active.
1 parent 0eafc73 commit 23eef9a

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() &&
@@ -5408,6 +5373,53 @@ bool Compiler<Emitter>::maybeEmitDeferredVarInit(const VarDecl *VD) {
54085373
return true;
54095374
}
54105375

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

59425954
if (OptPrimType T = this->classify(InitExpr)) {
5955+
if (Activate && !this->emitActivateThisField(FieldOffset, InitExpr))
5956+
return false;
5957+
59435958
if (!this->visit(InitExpr))
59445959
return false;
59455960

59465961
bool BitField = F->isBitField();
5947-
if (BitField && Activate)
5948-
return this->emitInitThisBitFieldActivate(*T, F, FieldOffset, InitExpr);
59495962
if (BitField)
59505963
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
5951-
if (Activate)
5952-
return this->emitInitThisFieldActivate(*T, FieldOffset, InitExpr);
59535964
return this->emitInitThisField(*T, FieldOffset, InitExpr);
59545965
}
59555966
// 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
@@ -401,6 +401,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
401401
bool checkLiteralType(const Expr *E);
402402
bool maybeEmitDeferredVarInit(const VarDecl *VD);
403403

404+
bool refersToUnion(const Expr *E);
405+
404406
protected:
405407
/// Variable to storage mapping.
406408
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)