-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[clang][bytecode] Activate primitive fields before initializing them #149963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) ChangesThe initializer itself might need the field to be active. Full diff: https://github.com/llvm/llvm-project/pull/149963.diff 4 Files Affected:
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 952a43a0ecbcf..af2f69bc8573e 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -25,11 +25,18 @@ using APSInt = llvm::APSInt;
namespace clang {
namespace interp {
+static bool hasTrivialDefaultCtorParent(const FieldDecl *FD) {
+ assert(FD);
+ assert(FD->getParent()->isUnion());
+ const auto *CXXRD = dyn_cast<CXXRecordDecl>(FD->getParent());
+ return !CXXRD || CXXRD->hasTrivialDefaultConstructor();
+}
+
static bool refersToUnion(const Expr *E) {
for (;;) {
if (const auto *ME = dyn_cast<MemberExpr>(E)) {
if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
- FD && FD->getParent()->isUnion())
+ FD && FD->getParent()->isUnion() && hasTrivialDefaultCtorParent(FD))
return true;
E = ME->getBase();
continue;
@@ -5934,16 +5941,15 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
return false;
if (std::optional<PrimType> T = this->classify(InitExpr)) {
+ if (Activate && !this->emitActivateThisField(FieldOffset, InitExpr))
+ return false;
+
if (!this->visit(InitExpr))
return false;
bool BitField = F->isBitField();
- if (BitField && Activate)
- return this->emitInitThisBitFieldActivate(*T, F, FieldOffset, InitExpr);
if (BitField)
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
- if (Activate)
- return this->emitInitThisFieldActivate(*T, FieldOffset, InitExpr);
return this->emitInitThisField(*T, FieldOffset, InitExpr);
}
// Non-primitive case. Get a pointer to the field-to-initialize
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 7f29200f8617f..a234f8ebf0c82 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1983,6 +1983,16 @@ static inline bool Activate(InterpState &S, CodePtr OpPC) {
return true;
}
+static inline bool ActivateThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+
+ const Pointer &Ptr = S.Current->getThis();
+ assert(Ptr.atField(I).canBeInitialized());
+ Ptr.atField(I).activate();
+ return true;
+}
+
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool StoreActivate(InterpState &S, CodePtr OpPC) {
const T &Value = S.Stk.pop<T>();
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 80703ad72d954..abfed77750f87 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -510,6 +510,7 @@ def StoreBitFieldActivate : StoreBitFieldOpcode {}
def StoreBitFieldActivatePop : StoreBitFieldOpcode {}
def Activate : Opcode {}
+def ActivateThisField : Opcode { let Args = [ArgUint32]; }
// [Pointer, Value] -> []
def Init : StoreOpcode {}
diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp
index 7cfd0d677a7b3..8fce2e6a738c7 100644
--- a/clang/test/AST/ByteCode/unions.cpp
+++ b/clang/test/AST/ByteCode/unions.cpp
@@ -79,10 +79,9 @@ namespace DefaultInit {
constexpr U1 u1; /// OK.
- constexpr int foo() { // expected-error {{never produces a constant expression}}
+ constexpr int foo() {
U1 u;
- return u.a; // both-note {{read of member 'a' of union with active member 'b'}} \
- // expected-note {{read of member 'a' of union with active member 'b'}}
+ return u.a; // both-note {{read of member 'a' of union with active member 'b'}}
}
static_assert(foo() == 42); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}
@@ -861,6 +860,71 @@ namespace CopyCtorMutable {
// both-note {{in call}}
}
+
+namespace NonTrivialCtor {
+ struct A { int x = 1; constexpr int f() { return 1; } };
+ struct B : A { int y = 1; constexpr int g() { return 2; } };
+ struct C {
+ int x;
+ constexpr virtual int f() = 0;
+ };
+ struct D : C {
+ int y;
+ constexpr virtual int f() override { return 3; }
+ };
+
+ union U {
+ int n;
+ B b;
+ D d;
+ };
+
+ consteval int test(int which) {
+ if (which == 0) {}
+
+ U u{.n = 5};
+ assert_active(u);
+ assert_active(u.n);
+ assert_inactive(u.b);
+
+ switch (which) {
+ case 0:
+ u.b.x = 10; // both-note {{assignment to member 'b' of union with active member 'n'}}
+ return u.b.f();
+ case 1:
+ u.b.y = 10; // both-note {{assignment to member 'b' of union with active member 'n'}}
+ return u.b.g();
+ case 2:
+ u.d.x = 10; // both-note {{assignment to member 'd' of union with active member 'n'}}
+ return u.d.f();
+ case 3:
+ u.d.y = 10; // both-note {{assignment to member 'd' of union with active member 'n'}}
+ return u.d.f();
+ }
+
+ return 1;
+ }
+ static_assert(test(0)); // both-error {{not an integral constant expression}} \
+ // both-note {{in call}}
+ static_assert(test(1)); // both-error {{not an integral constant expression}} \
+ // both-note {{in call}}
+ static_assert(test(2)); // both-error {{not an integral constant expression}} \
+ // both-note {{in call}}
+ static_assert(test(3)); // both-error {{not an integral constant expression}} \
+ // both-note {{in call}}
+
+}
+
+namespace PrimitiveFieldInitActivates {
+ /// The initializer of a needs the field to be active _before_ it's visited.
+ template<int> struct X {};
+ union V {
+ int a, b;
+ constexpr V(X<0>) : a(a = 1) {} // ok
+ };
+ constinit V v0 = X<0>();
+}
+
#endif
namespace AddressComparison {
|
2d50e77
to
67dd9f5
Compare
The initializer itself might need the field to be active.
67dd9f5
to
97bbf08
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
clang:bytecode
Issues for the clang bytecode constexpr interpreter
clang:frontend
Language frontend issues, e.g. anything involving "Sema"
clang
Clang issues not falling into any other category
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The initializer itself might need the field to be active.