[clang] [clang][bytecode] Diagnose member calls on deleted blocks (PR #106529)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 29 04:06:34 PDT 2024


https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/106529

This requires a bit of restructuring of ctor calls when checking for a potential constant expression.

>From 86f776e9de9ddee4bb5c35f6f6cd7959795bea8c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 29 Aug 2024 12:54:10 +0200
Subject: [PATCH] [clang][bytecode] Diagnose member calls on deleted blocks

This requires a bit of restructuring of ctor calls when checking for a
potential constant expression.
---
 clang/lib/AST/ByteCode/Interp.cpp      | 16 ++++++++++------
 clang/lib/AST/ByteCode/Interp.h        |  6 +++++-
 clang/lib/AST/ByteCode/InterpBlock.cpp |  2 ++
 clang/lib/AST/ByteCode/Pointer.h       |  8 ++++++++
 clang/test/AST/ByteCode/new-delete.cpp |  7 +++++++
 clang/test/AST/ByteCode/unions.cpp     | 20 ++++++++------------
 6 files changed, 40 insertions(+), 19 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 09d3f4525138ed..51490cf46790b7 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -305,14 +305,18 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
 
   if (!Ptr.isLive()) {
     const auto &Src = S.Current->getSource(OpPC);
-    bool IsTemp = Ptr.isTemporary();
 
-    S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
+    if (Ptr.isDynamic()) {
+      S.FFDiag(Src, diag::note_constexpr_access_deleted_object) << AK;
+    } else {
+      bool IsTemp = Ptr.isTemporary();
+      S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
 
-    if (IsTemp)
-      S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
-    else
-      S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
+      if (IsTemp)
+        S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
+      else
+        S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
+    }
 
     return false;
   }
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 242532a3f0544e..ea868e21159c69 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -2623,7 +2623,11 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
   if (!CheckCallable(S, OpPC, Func))
     return false;
 
-  if (Func->hasThisPointer() && S.checkingPotentialConstantExpression())
+  // FIXME: The isConstructor() check here is not always right. The current
+  // constant evaluator is somewhat inconsistent in when it allows a function
+  // call when checking for a constant expression.
+  if (Func->hasThisPointer() && S.checkingPotentialConstantExpression() &&
+      !Func->isConstructor())
     return false;
 
   if (!CheckCallDepth(S, OpPC))
diff --git a/clang/lib/AST/ByteCode/InterpBlock.cpp b/clang/lib/AST/ByteCode/InterpBlock.cpp
index 7a3962290edb4e..0ce88ca7e52365 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.cpp
+++ b/clang/lib/AST/ByteCode/InterpBlock.cpp
@@ -110,6 +110,8 @@ DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
   Prev = nullptr;
   Root = this;
 
+  B.IsDynamic = Blk->IsDynamic;
+
   // Transfer pointers.
   B.Pointers = Blk->Pointers;
   for (Pointer *P = Blk->Pointers; P; P = P->Next)
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 27ac33616f5a8b..ef90e6e0dd7bd1 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -491,6 +491,14 @@ class Pointer {
     }
     return false;
   }
+  /// Checks if the storage has been dynamically allocated.
+  bool isDynamic() const {
+    if (isBlockPointer()) {
+      assert(asBlockPointer().Pointee);
+      return asBlockPointer().Pointee->isDynamic();
+    }
+    return false;
+  }
   /// Checks if the storage is a static temporary.
   bool isStaticTemporary() const { return isStatic() && isTemporary(); }
 
diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index d733e3182fd59c..145bb366710f9b 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -579,6 +579,13 @@ namespace CastedDelete {
                                  // expected-note {{in call to}}
 }
 
+constexpr void use_after_free_2() { // both-error {{never produces a constant expression}}
+  struct X { constexpr void f() {} };
+  X *p = new X;
+  delete p;
+  p->f(); // both-note {{member call on heap allocated object that has been deleted}}
+}
+
 #else
 /// Make sure we reject this prior to C++20
 constexpr int a() { // both-error {{never produces a constant expression}}
diff --git a/clang/test/AST/ByteCode/unions.cpp b/clang/test/AST/ByteCode/unions.cpp
index 35b4a520baa269..7b39bb1bb9316e 100644
--- a/clang/test/AST/ByteCode/unions.cpp
+++ b/clang/test/AST/ByteCode/unions.cpp
@@ -86,7 +86,7 @@ namespace DefaultInit {
 
 #if __cplusplus >= 202002L
 namespace SimpleActivate {
-  constexpr int foo() { // ref-error {{never produces a constant expression}}
+  constexpr int foo() { // both-error {{never produces a constant expression}}
     union {
       int a;
       int b;
@@ -94,8 +94,7 @@ namespace SimpleActivate {
 
     Z.a = 10;
     Z.b = 20;
-    return Z.a; // both-note {{read of member 'a' of union with active member 'b'}} \
-                // ref-note {{read of member 'a' of union with active member 'b}}
+    return Z.a; // both-note 2{{read of member 'a' of union with active member 'b'}}
   }
   static_assert(foo() == 20); // both-error {{not an integral constant expression}} \
                               // both-note {{in call to}}
@@ -212,11 +211,10 @@ namespace Nested {
     int y;
   };
 
- constexpr int foo() { // ref-error {{constexpr function never produces a constant expression}}
+ constexpr int foo() { // both-error {{constexpr function never produces a constant expression}}
     U2 u;
     u.u.a = 10;
-    int a = u.y; // both-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}} \
-                 // ref-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}}
+    int a = u.y; // both-note 2{{read of member 'y' of union with active member 'u' is not allowed in a constant expression}}
 
     return 1;
   }
@@ -230,24 +228,22 @@ namespace Nested {
   }
   static_assert(foo2() == 10);
 
- constexpr int foo3() { // ref-error {{constexpr function never produces a constant expression}}
+ constexpr int foo3() { // both-error {{constexpr function never produces a constant expression}}
     U2 u;
     u.u.a = 10;
-    int a = u.u.b; // both-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}} \
-                   // ref-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}}
+    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;
   }
   static_assert(foo3() == 1); // both-error {{not an integral constant expression}} \
                               // both-note {{in call to}}
 
-  constexpr int foo4() { // ref-error {{constexpr function never produces a constant expression}}
+  constexpr int foo4() { // both-error {{constexpr function never produces a constant expression}}
     U2 u;
 
     u.x = 10;
 
-    return u.u.a;// both-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}} \
-                 // ref-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}}
+    return u.u.a; // both-note 2{{read of member 'u' of union with active member 'x' is not allowed in a constant expression}}
   }
   static_assert(foo4() == 1); // both-error {{not an integral constant expression}} \
                               // both-note {{in call to}}



More information about the cfe-commits mailing list