Skip to content

Commit

Permalink
[clang][Interp] This pointers are writable in de-/constructors
Browse files Browse the repository at this point in the history
This is possible in C++20, so we need to check this when doing stores.

Differential Revision: https://reviews.llvm.org/D136751
  • Loading branch information
tbaederr committed Mar 1, 2023
1 parent 4e4ad3a commit 8d09bd6
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 1 deletion.
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Function.h
Expand Up @@ -134,6 +134,8 @@ class Function final {

/// Checks if the function is a constructor.
bool isConstructor() const { return isa<CXXConstructorDecl>(F); }
/// Checks if the function is a destructor.
bool isDestructor() const { return isa<CXXDestructorDecl>(F); }

/// Checks if the function is fully done compiling.
bool isFullyCompiled() const { return IsFullyCompiled; }
Expand Down
9 changes: 8 additions & 1 deletion clang/lib/AST/Interp/Interp.cpp
Expand Up @@ -213,7 +213,14 @@ bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,

bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
assert(Ptr.isLive() && "Pointer is not live");
if (!Ptr.isConst()) {
if (!Ptr.isConst())
return true;

// The This pointer is writable in constructors and destructors,
// even if isConst() returns true.
if (const Function *Func = S.Current->getFunction();
Func && (Func->isConstructor() || Func->isDestructor()) &&
Ptr.block() == S.Current->getThis().block()) {
return true;
}

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Pointer.h
Expand Up @@ -290,6 +290,8 @@ class Pointer {
/// Returns the number of elements.
unsigned getNumElems() const { return getSize() / elemSize(); }

Block *block() const { return Pointee; }

/// Returns the index into an array.
int64_t getIndex() const {
if (isElementPastEnd())
Expand Down
71 changes: 71 additions & 0 deletions clang/test/AST/Interp/cxx20.cpp
Expand Up @@ -201,3 +201,74 @@ constexpr Derived D; // expected-error {{must be initialized by a constant expre
// ref-note {{subobject of type 'bool' is not initialized}}
#endif
};

namespace ConstThis {
class Foo {
const int T = 12; // expected-note {{declared const here}} \
// ref-note {{declared const here}}
int a;
public:
constexpr Foo() {
this->a = 10;
T = 13; // expected-error {{cannot assign to non-static data member 'T' with const-qualified type}} \
// ref-error {{cannot assign to non-static data member 'T' with const-qualified type}}
}
};
constexpr Foo F; // expected-error {{must be initialized by a constant expression}} \
// ref-error {{must be initialized by a constant expression}}


class FooDtor {
int a;
public:
constexpr FooDtor() {
this->a = 10;
}
constexpr ~FooDtor() {
this->a = 12;
}
};

constexpr int foo() {
const FooDtor f;
return 0;
}
static_assert(foo() == 0);

template <bool Good>
struct ctor_test {
int a = 0;

constexpr ctor_test() {
if (Good)
a = 10;
int local = 100 / a; // expected-note {{division by zero}} \
// ref-note {{division by zero}}
}
};

template <bool Good>
struct dtor_test {
int a = 0;

constexpr dtor_test() = default;
constexpr ~dtor_test() {
if (Good)
a = 10;
int local = 100 / a; // expected-note {{division by zero}} \
// ref-note {{division by zero}}
}
};

constexpr ctor_test<true> good_ctor;
constexpr dtor_test<true> good_dtor;

constexpr ctor_test<false> bad_ctor; // expected-error {{must be initialized by a constant expression}} \
// expected-note {{in call to}} \
// ref-error {{must be initialized by a constant expression}} \
// ref-note {{in call to}}
constexpr dtor_test<false> bad_dtor; // expected-error {{must have constant destruction}} \
// expected-note {{in call to}} \
// ref-error {{must have constant destruction}} \
// ref-note {{in call to}}
};

0 comments on commit 8d09bd6

Please sign in to comment.