Skip to content

Commit b7660a5

Browse files
authored
[clang][bytecode] Fix const-in-mutable fields (#149286)
For mutable and const fields, we have two bits in InlineDescriptor, which both get inherited down the hierarchy. When a field is both const and mutable, we CAN read from it if it is a mutable-in-const field, but we _can't_ read from it if it is a const-in-mutable field. We need another bit to distinguish the two cases.
1 parent 3bb4355 commit b7660a5

File tree

6 files changed

+64
-9
lines changed

6 files changed

+64
-9
lines changed

clang/lib/AST/ByteCode/Descriptor.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
162162
Desc->IsConst = IsConst || D->IsConst;
163163
Desc->IsFieldMutable = IsMutable || D->IsMutable;
164164
Desc->IsVolatile = IsVolatile || D->IsVolatile;
165+
// True if this field is const AND the parent is mutable.
166+
Desc->IsConstInMutable = Desc->IsConst && IsMutable;
165167

166168
if (auto Fn = D->CtorFn)
167169
Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,

clang/lib/AST/ByteCode/Descriptor.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ struct InlineDescriptor {
101101
/// Flag indicating if the field is mutable (if in a record).
102102
LLVM_PREFERRED_TYPE(bool)
103103
unsigned IsFieldMutable : 1;
104+
/// Flag indicating if this field is a const field nested in
105+
/// a mutable parent field.
106+
LLVM_PREFERRED_TYPE(bool)
107+
unsigned IsConstInMutable : 1;
104108
/// Flag indicating if the field is an element of a composite array.
105109
LLVM_PREFERRED_TYPE(bool)
106110
unsigned IsArrayElement : 1;

clang/lib/AST/ByteCode/Disasm.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const {
445445
OS << "InUnion: " << InUnion << "\n";
446446
OS << "IsFieldMutable: " << IsFieldMutable << "\n";
447447
OS << "IsArrayElement: " << IsArrayElement << "\n";
448+
OS << "IsConstInMutable: " << IsConstInMutable << '\n';
448449
OS << "Desc: ";
449450
if (Desc)
450451
Desc->dump(OS);

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,10 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
566566

567567
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
568568
assert(Ptr.isLive() && "Pointer is not live");
569-
if (!Ptr.isConst() || Ptr.isMutable())
569+
if (!Ptr.isConst())
570+
return true;
571+
572+
if (Ptr.isMutable() && !Ptr.isConstInMutable())
570573
return true;
571574

572575
if (!Ptr.isBlockPointer())

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,11 @@ class Pointer {
576576
return true;
577577
return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
578578
}
579+
bool isConstInMutable() const {
580+
if (!isBlockPointer())
581+
return false;
582+
return isRoot() ? false : getInlineDesc()->IsConstInMutable;
583+
}
579584

580585
/// Checks if an object or a subfield is volatile.
581586
bool isVolatile() const {

clang/test/AST/ByteCode/mutable.cpp

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++11 -verify=expected,expected11,both,both11 %s
2-
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++14 -verify=expected,expected14,both %s
3-
// RUN: %clang_cc1 -std=c++11 -verify=ref,ref11,both,both11 %s
4-
// RUN: %clang_cc1 -std=c++14 -verify=ref,ref14,both %s
5-
6-
7-
8-
1+
// RUN: %clang_cc1 -std=c++11 -verify=expected,expected11,both,both11 %s -fexperimental-new-constant-interpreter
2+
// RUN: %clang_cc1 -std=c++14 -verify=expected,expected14,both %s -fexperimental-new-constant-interpreter
3+
// RUN: %clang_cc1 -std=c++11 -verify=ref,ref11,both,both11 %s
4+
// RUN: %clang_cc1 -std=c++14 -verify=ref,ref14,both %s
95

106
namespace Simple {
117
struct S {
@@ -26,3 +22,47 @@ namespace Simple {
2622
static_assert(s2.a2 == 12, ""); // both11-error {{not an integral constant expression}} \
2723
// both11-note {{initializer of 's2' is not a constant expression}}
2824
}
25+
#if __cplusplus >= 201402L
26+
namespace ConstInMutable {
27+
class B {
28+
public:
29+
30+
const int f;
31+
constexpr B() : f(12) {}
32+
};
33+
class A {
34+
public:
35+
mutable B b;
36+
constexpr A() = default;
37+
};
38+
constexpr int constInMutable() {
39+
A a;
40+
41+
int *m = (int*)&a.b.f;
42+
*m = 12; // both-note {{modification of object of const-qualified type 'const int' is not allowed in a constant expression}}
43+
return 1;
44+
}
45+
static_assert(constInMutable() == 1, ""); // both-error {{not an integral constant expression}} \
46+
// both-note {{in call to}}
47+
}
48+
49+
namespace MutableInConst {
50+
class C {
51+
public:
52+
mutable int c;
53+
constexpr C() : c(50) {}
54+
};
55+
class D {
56+
public:
57+
C c;
58+
constexpr D() {}
59+
};
60+
constexpr int mutableInConst() {
61+
const D d{};
62+
int *m = (int*)&d.c.c;
63+
*m = 12;
64+
return 1;
65+
}
66+
static_assert(mutableInConst() == 1, "");
67+
}
68+
#endif

0 commit comments

Comments
 (0)