aboutsummaryrefslogtreecommitdiff
path: root/clang/test/AST/ByteCode
diff options
context:
space:
mode:
authorTimm Baeder <tbaeder@redhat.com>2025-07-16 09:03:33 +0200
committerGitHub <noreply@github.com>2025-07-16 09:03:33 +0200
commit17d3029331a65ea54ad1bdbf79add03d5a71dcd4 (patch)
treeed5ac24acee81c602ddd4dac04d0644431d70032 /clang/test/AST/ByteCode
parent871038759afb491c16fb2cd14b78e51e410efbf3 (diff)
downloadllvm-17d3029331a65ea54ad1bdbf79add03d5a71dcd4.zip
llvm-17d3029331a65ea54ad1bdbf79add03d5a71dcd4.tar.gz
llvm-17d3029331a65ea54ad1bdbf79add03d5a71dcd4.tar.bz2
[clang][bytecode] Make union activation more granular (#148835)
Only activate things if the syntactical structure suggests so. This adds a bunch of new opcodes to control whether to activate in stores, etc. Fixes #134789
Diffstat (limited to 'clang/test/AST/ByteCode')
-rw-r--r--clang/test/AST/ByteCode/cxx23.cpp19
-rw-r--r--clang/test/AST/ByteCode/unions.cpp141
2 files changed, 157 insertions, 3 deletions
diff --git a/clang/test/AST/ByteCode/cxx23.cpp b/clang/test/AST/ByteCode/cxx23.cpp
index 2856b87..45dd4f5 100644
--- a/clang/test/AST/ByteCode/cxx23.cpp
+++ b/clang/test/AST/ByteCode/cxx23.cpp
@@ -369,7 +369,26 @@ namespace NestedUnions {
return true;
}
static_assert(test_nested());
+}
+
+namespace UnionMemberCallDiags {
+ struct A { int n; };
+ struct B { A a; };
+ constexpr A a = (A() = B().a);
+ union C {
+ int n;
+ A a;
+ };
+ constexpr bool g() {
+ C c = {.n = 1};
+ c.a.operator=(B{2}.a); // all-note {{member call on member 'a' of union with active member 'n' is not allowed in a constant expression}}
+ return c.a.n == 2;
+ }
+ static_assert(g()); // all-error {{not an integral constant expression}} \
+ // all-note {{in call to}}
}
+
+
#endif
diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp
index f97990c..0fa44a25 100644
--- a/clang/test/AST/ByteCode/unions.cpp
+++ b/clang/test/AST/ByteCode/unions.cpp
@@ -3,6 +3,9 @@
// RUN: %clang_cc1 -verify=ref,both %s
// RUN: %clang_cc1 -std=c++20 -verify=ref,both %s
+#define assert_active(F) if (!__builtin_is_within_lifetime(&F)) (1/0);
+#define assert_inactive(F) if ( __builtin_is_within_lifetime(&F)) (1/0);
+
union U {
int a;
int b;
@@ -229,9 +232,24 @@ namespace Nested {
}
static_assert(foo2() == 10);
- constexpr int foo3() { // both-error {{constexpr function never produces a constant expression}}
+ consteval int foo3() { // both-error {{function never produces a constant expression}}
U2 u;
+ /// No active field.
+ assert_active(u);
+ assert_inactive(u.u);
+ assert_inactive(u.u2);
+ assert_inactive(u.x);
+ assert_inactive(u.y);
+
u.u.a = 10;
+ assert_active(u);
+ assert_active(u.u);
+ assert_active(u.u.a);
+ assert_inactive(u.u.b);
+ assert_inactive(u.u2);
+ assert_inactive(u.x);
+ assert_inactive(u.y);
+
int a = u.u.b; // both-note 2{{read of member 'b' of union with active member 'a' is not allowed in a constant expression}}
return 1;
@@ -343,6 +361,21 @@ namespace IndirectField {
static_assert(s2.f == 7, "");
}
+namespace CtorActivatesFields {
+ struct TailClobberer {
+ constexpr TailClobberer() { b = false; }
+ bool b;
+ };
+
+ class expected {
+ union __union_t {
+ constexpr __union_t() : __unex_() {}
+ TailClobberer __unex_;
+ } __union_;
+ };
+ constexpr expected y;
+}
+
namespace CopyCtor {
union U {
int a;
@@ -579,6 +612,7 @@ namespace MoveOrAssignOp {
};
class F {
+ public:
struct __long {
min_pointer __data_;
};
@@ -599,6 +633,11 @@ namespace MoveOrAssignOp {
return true;
}
static_assert(foo());
+
+ constexpr F f2{};
+ static_assert(__builtin_is_within_lifetime(&f2.__rep_));
+ static_assert(__builtin_is_within_lifetime(&f2.__rep_.__l));
+ static_assert(__builtin_is_within_lifetime(&f2.__rep_.__l.__data_));
}
namespace CopyEmptyUnion {
@@ -683,8 +722,6 @@ namespace AnonymousUnion {
Long L;
};
-#define assert_active(F) if (!__builtin_is_within_lifetime(&F)) (1/0);
-#define assert_inactive(F) if ( __builtin_is_within_lifetime(&F)) (1/0);
consteval int test() {
union UU {
struct {
@@ -712,6 +749,104 @@ namespace AnonymousUnion {
}
static_assert(test() == 1);
}
+
+namespace AccessViaPointer {
+ struct A {
+ int x;
+ int y;
+ int arr[3];
+ union { int p, q; };
+ };
+ union B {
+ A a;
+ int b;
+ };
+
+ constexpr int write_wrong_member_indirect() { // both-error {{never produces a constant}}
+ B b = {.b = 1};
+ int *p = &b.a.y;
+
+ *p = 12; // both-note 2{{assignment to member 'a' of union with active member 'b'}}
+
+ return *p;
+ }
+ static_assert(write_wrong_member_indirect() == 1); // both-error {{not an integral constant expression}} \
+ // both-note {{in call to}}
+}
+
+namespace Activation {
+ union U {
+ int a;
+ int b;
+ };
+
+ struct S { int& b; };
+
+ constexpr int foo() { // both-error {{never produces a constant expression}}
+ U u;
+ u.a = 10;
+ S s{u.b};
+
+ // LHS is a MemberExpr, but not of a union type. shouldn't activate u.b.
+ s.b = 12; // both-note 2{{assignment to member 'b' of union with active member 'a'}}
+
+ return u.b;
+
+ }
+ static_assert(foo() == 12); // both-error {{not an integral constant expression}} \
+ // both-note {{in call to}}
+
+ struct SS {
+ int a;
+ consteval SS() {
+ a = 10;
+ }
+ };
+
+ /// Activating the struct should also activate all the struct members.
+ consteval int structInUnion() {
+ union {
+ SS s;
+ int b;
+ } u{};
+
+ // assert_active(u.s);
+ // assert_active(u.s.a);
+ //assert_inactive(u.b);
+
+ return u.s.a;
+ }
+ static_assert(structInUnion() == 10);
+
+}
+
+namespace Activation2 {
+ struct Base {
+ int y;
+ };
+ struct A : Base {
+ int x;
+ int arr[3];
+ union { int p, q; };
+ };
+ union B {
+ A a;
+ int b;
+ };
+
+ constexpr int change_member_indirectly() {
+ B b = {.b = 1};
+ b.a.arr[1] = 1;
+ int &r = b.a.y;
+ r = 123;
+
+ b.b = 2;
+ b.a.y = 3;
+ b.a.arr[2] = 4;
+ return b.a.arr[2];
+ }
+ static_assert(change_member_indirectly() == 4);
+}
#endif
namespace AddressComparison {