diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -134,6 +134,8 @@ /// Checks if the function is a constructor. bool isConstructor() const { return isa(F); } + /// Checks if the function is a destructor. + bool isDestructor() const { return isa(F); } /// Checks if the function is fully done compiling. bool isFullyCompiled() const { return IsFullyCompiled; } diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -213,7 +213,14 @@ 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; } diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h --- a/clang/lib/AST/Interp/Pointer.h +++ b/clang/lib/AST/Interp/Pointer.h @@ -290,6 +290,8 @@ /// 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()) diff --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp --- a/clang/test/AST/Interp/cxx20.cpp +++ b/clang/test/AST/Interp/cxx20.cpp @@ -201,3 +201,74 @@ // 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 + 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 + 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 good_ctor; + constexpr dtor_test good_dtor; + + constexpr ctor_test 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 bad_dtor; // expected-error {{must have constant destruction}} \ + // expected-note {{in call to}} \ + // ref-error {{must have constant destruction}} \ + // ref-note {{in call to}} +};