[clang] 8d09bd6 - [clang][Interp] This pointers are writable in de-/constructors

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 1 06:41:33 PST 2023


Author: Timm Bäder
Date: 2023-03-01T15:41:10+01:00
New Revision: 8d09bd616be8e347d2769d7b17cd8e717c855c45

URL: https://github.com/llvm/llvm-project/commit/8d09bd616be8e347d2769d7b17cd8e717c855c45
DIFF: https://github.com/llvm/llvm-project/commit/8d09bd616be8e347d2769d7b17cd8e717c855c45.diff

LOG: [clang][Interp] This pointers are writable in de-/constructors

This is possible in C++20, so we need to check this when doing stores.

Differential Revision: https://reviews.llvm.org/D136751

Added: 
    

Modified: 
    clang/lib/AST/Interp/Function.h
    clang/lib/AST/Interp/Interp.cpp
    clang/lib/AST/Interp/Pointer.h
    clang/test/AST/Interp/cxx20.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h
index 1eb4fa90d794..422211708a77 100644
--- a/clang/lib/AST/Interp/Function.h
+++ b/clang/lib/AST/Interp/Function.h
@@ -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; }

diff  --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 319aba0ffa94..329cc0ff949d 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -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;
   }
 

diff  --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index 489884a25659..5895c61295e0 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -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())

diff  --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp
index e9505b3cf718..44c97dd1afcd 100644
--- a/clang/test/AST/Interp/cxx20.cpp
+++ b/clang/test/AST/Interp/cxx20.cpp
@@ -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}}
+};


        


More information about the cfe-commits mailing list