[clang] 4b96400 - [clang][bytecode] Allow placement-new in std functions pre-C++26 (#109753)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 24 01:28:57 PDT 2024


Author: Timm Baeder
Date: 2024-09-24T10:28:54+02:00
New Revision: 4b964002403a9b9be934174391ff5b698691a26b

URL: https://github.com/llvm/llvm-project/commit/4b964002403a9b9be934174391ff5b698691a26b
DIFF: https://github.com/llvm/llvm-project/commit/4b964002403a9b9be934174391ff5b698691a26b.diff

LOG: [clang][bytecode] Allow placement-new in std functions pre-C++26 (#109753)

Added: 
    

Modified: 
    clang/lib/AST/ByteCode/Compiler.cpp
    clang/lib/AST/ByteCode/Interp.cpp
    clang/lib/AST/ByteCode/InterpBuiltin.cpp
    clang/lib/AST/ByteCode/InterpFrame.cpp
    clang/lib/AST/ByteCode/InterpFrame.h
    clang/test/AST/ByteCode/new-delete.cpp
    clang/test/AST/ByteCode/placement-new.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 939a3aa43ff8dd..754cd0db9868b7 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3117,21 +3117,22 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
         if (!this->discard(Arg1))
           return false;
         IsNoThrow = true;
-      } else if (Ctx.getLangOpts().CPlusPlus26 &&
-                 OperatorNew->isReservedGlobalPlacementOperator()) {
+      } else {
+        // Invalid unless we have C++26 or are in a std:: function.
+        if (!this->emitInvalidNewDeleteExpr(E, E))
+          return false;
+
         // If we have a placement-new destination, we'll later use that instead
         // of allocating.
-        PlacementDest = Arg1;
-      } else {
-        return this->emitInvalidNewDeleteExpr(E, E);
+        if (OperatorNew->isReservedGlobalPlacementOperator())
+          PlacementDest = Arg1;
       }
-
     } else {
+      // Always invalid.
       return this->emitInvalid(E);
     }
-  } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
+  } else if (!OperatorNew->isReplaceableGlobalAllocationFunction())
     return this->emitInvalidNewDeleteExpr(E, E);
-  }
 
   const Descriptor *Desc;
   if (!PlacementDest) {

diff  --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 739f6d2d8a7e95..8b578ccbeb6792 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1293,6 +1293,13 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
   if (!CheckStore(S, OpPC, Ptr))
     return false;
 
+  if (!InvalidNewDeleteExpr(S, OpPC, E))
+    return false;
+
+  // Assume proper types in std functions.
+  if (S.Current->isStdFunction())
+    return true;
+
   const auto *NewExpr = cast<CXXNewExpr>(E);
   QualType StorageType = Ptr.getType();
 
@@ -1334,10 +1341,16 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
   assert(E);
   const auto &Loc = S.Current->getSource(OpPC);
 
+  if (S.getLangOpts().CPlusPlus26)
+    return true;
+
   if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) {
     const FunctionDecl *OperatorNew = NewExpr->getOperatorNew();
 
     if (!S.getLangOpts().CPlusPlus26 && NewExpr->getNumPlacementArgs() > 0) {
+      // This is allowed pre-C++26, but only an std function.
+      if (S.Current->isStdFunction())
+        return true;
       S.FFDiag(Loc, diag::note_constexpr_new_placement)
           << /*C++26 feature*/ 1 << E->getSourceRange();
     } else if (NewExpr->getNumPlacementArgs() == 1 &&

diff  --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 523f5cb993dbc7..68710f67be2003 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -1345,8 +1345,7 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
   assert(!ElemT);
   // Structs etc.
   const Descriptor *Desc = S.P.createDescriptor(
-      Call, ElemType.getTypePtr(),
-      NumElems.ule(1) ? std::nullopt : Descriptor::InlineDescMD,
+      Call, ElemType.getTypePtr(), Descriptor::InlineDescMD,
       /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false,
       /*Init=*/nullptr);
 

diff  --git a/clang/lib/AST/ByteCode/InterpFrame.cpp b/clang/lib/AST/ByteCode/InterpFrame.cpp
index 7c877a70fe6b97..7f02464a1c0f14 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.cpp
+++ b/clang/lib/AST/ByteCode/InterpFrame.cpp
@@ -257,3 +257,13 @@ SourceRange InterpFrame::getRange(CodePtr PC) const {
 
   return S.getRange(Func, PC);
 }
+
+bool InterpFrame::isStdFunction() const {
+  if (!Func)
+    return false;
+  for (const DeclContext *DC = Func->getDecl(); DC; DC = DC->getParent())
+    if (DC->isStdNamespace())
+      return true;
+
+  return false;
+}

diff  --git a/clang/lib/AST/ByteCode/InterpFrame.h b/clang/lib/AST/ByteCode/InterpFrame.h
index 802777a523d9b3..7cfc3ac68b4f3e 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.h
+++ b/clang/lib/AST/ByteCode/InterpFrame.h
@@ -117,6 +117,8 @@ class InterpFrame final : public Frame {
 
   unsigned getDepth() const { return Depth; }
 
+  bool isStdFunction() const;
+
   void dump() const { dump(llvm::errs(), 0); }
   void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const;
 

diff  --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index d62f12b63eee8e..8c9d5d9c9b1d7c 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -594,6 +594,10 @@ namespace std {
                                     // both-note {{used to delete a null pointer}}
     }
   };
+  template<typename T, typename ...Args>
+  constexpr void construct_at(void *p, Args &&...args) { // #construct
+    new (p) T((Args&&)args...);
+  }
 }
 
 /// Specialization for float, using operator new/delete.
@@ -762,6 +766,16 @@ namespace Placement {
   }
   static_assert(ok1()); // both-error {{not an integral constant expression}} \
                         // both-note {{in call to}}
+
+  /// placement-new should be supported before C++26 in std functions.
+  constexpr int ok2() {
+    int *I = new int;
+    std::construct_at<int>(I);
+    int r = *I;
+    delete I;
+    return r;
+  }
+  static_assert(ok2()== 0);
 }
 
 #else

diff  --git a/clang/test/AST/ByteCode/placement-new.cpp b/clang/test/AST/ByteCode/placement-new.cpp
index 9e86217c5fbf36..7a562adae02a6f 100644
--- a/clang/test/AST/ByteCode/placement-new.cpp
+++ b/clang/test/AST/ByteCode/placement-new.cpp
@@ -3,6 +3,18 @@
 
 namespace std {
   using size_t = decltype(sizeof(0));
+  template<typename T> struct allocator {
+    constexpr T *allocate(size_t N) {
+      return (T*)operator new(sizeof(T) * N);
+    }
+    constexpr void deallocate(void *p) {
+      operator delete(p);
+    }
+  };
+  template<typename T, typename ...Args>
+  constexpr void construct_at(void *p, Args &&...args) {
+    new (p) T((Args&&)args...); // both-note {{in call to}}
+  }
 }
 
 void *operator new(std::size_t, void *p) { return p; }
@@ -217,3 +229,35 @@ namespace records {
   }
   static_assert(foo() == 0);
 }
+
+namespace ConstructAt {
+  struct S {
+    int a = 10;
+    float b = 1.0;
+  };
+
+  constexpr bool ok1() {
+    S s;
+
+    std::construct_at<S>(&s);
+    return s.a == 10 && s.b == 1.0;
+  }
+  static_assert(ok1());
+
+  struct S2 {
+    constexpr S2() {
+      (void)(1/0); // both-note {{division by zero}} \
+                   // both-warning {{division by zero is undefined}}
+    }
+  };
+
+  constexpr bool ctorFail() { //
+    S2 *s = std::allocator<S2>().allocate(1);
+    std::construct_at<S2>(s); // both-note {{in call to}}
+
+    return true;
+  }
+  static_assert(ctorFail()); // both-error {{not an integral constant expression}} \
+                             // both-note {{in call to 'ctorFail()'}}
+
+}


        


More information about the cfe-commits mailing list