[flang-commits] [compiler-rt] [flang] [clang] [clang-tools-extra] [llvm] [clang] Add `::_placement_new` expression for built-in global placement new (PR #72209)

via flang-commits flang-commits at lists.llvm.org
Sat Nov 18 13:33:08 PST 2023


https://github.com/MaxEW707 updated https://github.com/llvm/llvm-project/pull/72209

>From 75cf305fe732d00be910a6aa0afe79953c5b7186 Mon Sep 17 00:00:00 2001
From: MaxEW707 <82551778+MaxEW707 at users.noreply.github.com>
Date: Sun, 12 Nov 2023 11:36:58 -0500
Subject: [PATCH 1/4] Implement `::_placement_new` expression for built-in
 global placement new without header includes, overload resolution or dealing
 with std libraries that declare the global placement new with attributes that
 cannot be redeclared.

---
 clang/include/clang/AST/ExprCXX.h             | 23 +++++++++
 clang/include/clang/AST/Stmt.h                |  4 ++
 .../clang/Basic/DiagnosticParseKinds.td       |  2 +
 clang/include/clang/Basic/TokenKinds.def      |  1 +
 clang/include/clang/Sema/Sema.h               |  4 +-
 clang/lib/AST/ASTImporter.cpp                 | 18 ++++---
 clang/lib/AST/ExprCXX.cpp                     | 34 +++++++++++++
 clang/lib/AST/ExprConstant.cpp                |  7 ++-
 clang/lib/AST/JSONNodeDumper.cpp              |  1 +
 clang/lib/AST/StmtPrinter.cpp                 |  5 +-
 clang/lib/AST/StmtProfile.cpp                 |  1 +
 clang/lib/AST/TextNodeDumper.cpp              |  2 +
 clang/lib/CodeGen/CGExprCXX.cpp               |  8 ++--
 clang/lib/CodeGen/CodeGenModule.cpp           |  5 +-
 clang/lib/Interpreter/Interpreter.cpp         |  4 +-
 clang/lib/Parse/ParseDecl.cpp                 |  6 ++-
 clang/lib/Parse/ParseExpr.cpp                 |  2 +-
 clang/lib/Parse/ParseExprCXX.cpp              | 19 ++++++--
 clang/lib/Parse/ParseObjc.cpp                 |  1 +
 clang/lib/Parse/ParseTentative.cpp            |  3 +-
 clang/lib/Sema/SemaExprCXX.cpp                | 48 ++++++++++++++-----
 clang/lib/Sema/TreeTransform.h                |  6 +--
 clang/lib/Serialization/ASTReaderStmt.cpp     |  2 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |  2 +
 .../Checkers/CheckPlacementNew.cpp            |  2 +-
 .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp |  9 ++--
 clang/test/AST/ast-dump-expr.cpp              | 14 ++++++
 clang/test/CodeGenCXX/new.cpp                 | 44 ++++++++++++++++-
 clang/test/SemaCXX/new-delete.cpp             |  7 +++
 29 files changed, 234 insertions(+), 50 deletions(-)

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 24278016431837b..d760af796aea28f 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2282,6 +2282,13 @@ class CXXNewExpr final
              QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
              SourceRange DirectInitRange);
 
+  /// Build a c++ builtin placement new expression
+  CXXNewExpr(Expr *PlacementArg,
+             SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
+             CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
+             QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
+             SourceRange DirectInitRange);
+
   /// Build an empty c++ new expression.
   CXXNewExpr(EmptyShell Empty, bool IsArray, unsigned NumPlacementArgs,
              bool IsParenTypeId);
@@ -2297,6 +2304,14 @@ class CXXNewExpr final
          QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
          SourceRange DirectInitRange);
 
+  /// Create a c++ builtin placement new expression.
+  static CXXNewExpr *
+  CreatePlacementNew(const ASTContext &Ctx, Expr *PlacementArg,
+                     SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
+                     CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
+                     QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
+                     SourceRange DirectInitRange);
+
   /// Create an empty c++ new expression.
   static CXXNewExpr *CreateEmpty(const ASTContext &Ctx, bool IsArray,
                                  bool HasInit, unsigned NumPlacementArgs,
@@ -2332,6 +2347,12 @@ class CXXNewExpr final
   FunctionDecl *getOperatorDelete() const { return OperatorDelete; }
   void setOperatorDelete(FunctionDecl *D) { OperatorDelete = D; }
 
+  bool isReservedPlacementNew() const {
+    if (CXXNewExprBits.IsPlacementNewExpr)
+      return true;
+    return OperatorNew->isReservedGlobalPlacementOperator();
+  }
+
   bool isArray() const { return CXXNewExprBits.IsArray; }
 
   /// This might return std::nullopt even if isArray() returns true,
@@ -2387,6 +2408,8 @@ class CXXNewExpr final
 
   bool isGlobalNew() const { return CXXNewExprBits.IsGlobalNew; }
 
+  bool isPlacementNewExpr() const { return CXXNewExprBits.IsPlacementNewExpr; }
+
   /// Whether this new-expression has any initializer at all.
   bool hasInitializer() const {
     switch (getInitializationStyle()) {
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index da7b37ce0e1211f..2dc3746aee4fd71 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -878,6 +878,10 @@ class alignas(void *) Stmt {
     LLVM_PREFERRED_TYPE(bool)
     unsigned IsParenTypeId : 1;
 
+    /// True is this if the builtin placement-new expression.
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned IsPlacementNewExpr : 1;
+
     /// The number of placement new arguments.
     unsigned NumPlacementArgs;
   };
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index de180344fcc5c74..c0ca7529fda892d 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -255,6 +255,8 @@ def err_expected_lbrace_in_compound_literal : Error<
   "expected '{' in compound literal">;
 def err_expected_while : Error<"expected 'while' in do/while loop">;
 
+def err_placement_new_expected_one_argument : Error<"expected only one argument in placement params">;
+
 def err_expected_semi_after_stmt : Error<"expected ';' after %0 statement">;
 def err_expected_semi_after_expr : Error<"expected ';' after expression">;
 def err_extraneous_token_before_semi : Error<"extraneous '%0' before ';'">;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 3ce317d318f9bb6..a2dfc1a9e08691a 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -365,6 +365,7 @@ KEYWORD(typeid                      , KEYCXX)
 KEYWORD(using                       , KEYCXX)
 KEYWORD(virtual                     , KEYCXX)
 KEYWORD(wchar_t                     , WCHARSUPPORT)
+KEYWORD(_placement_new              , KEYCXX)
 
 // C++ 2.5p2: Alternative Representations.
 CXX_KEYWORD_OPERATOR(and     , ampamp)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f69f366c1750918..ab9a0d0713435b6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6810,14 +6810,14 @@ class Sema final {
                                        bool ListInitialization);
 
   /// ActOnCXXNew - Parsed a C++ 'new' expression.
-  ExprResult ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
+  ExprResult ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, bool IsPlacementNewExpr,
                          SourceLocation PlacementLParen,
                          MultiExprArg PlacementArgs,
                          SourceLocation PlacementRParen,
                          SourceRange TypeIdParens, Declarator &D,
                          Expr *Initializer);
   ExprResult
-  BuildCXXNew(SourceRange Range, bool UseGlobal, SourceLocation PlacementLParen,
+  BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacementNewExpr, SourceLocation PlacementLParen,
               MultiExprArg PlacementArgs, SourceLocation PlacementRParen,
               SourceRange TypeIdParens, QualType AllocType,
               TypeSourceInfo *AllocTypeInfo, std::optional<Expr *> ArraySize,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index c4e931e220f69b5..3204fb69b3440f7 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8102,12 +8102,18 @@ ExpectedStmt ASTNodeImporter::VisitCXXNewExpr(CXXNewExpr *E) {
       ImportContainerChecked(E->placement_arguments(), ToPlacementArgs))
     return std::move(Err);
 
-  return CXXNewExpr::Create(
-      Importer.getToContext(), E->isGlobalNew(), ToOperatorNew,
-      ToOperatorDelete, E->passAlignment(), E->doesUsualArrayDeleteWantSize(),
-      ToPlacementArgs, ToTypeIdParens, ToArraySize, E->getInitializationStyle(),
-      ToInitializer, ToType, ToAllocatedTypeSourceInfo, ToSourceRange,
-      ToDirectInitRange);
+  if (E->isPlacementNewExpr())
+    return CXXNewExpr::CreatePlacementNew(
+        Importer.getToContext(), ToPlacementArgs[0], ToTypeIdParens, ToArraySize,
+        E->getInitializationStyle(), ToInitializer, ToType, ToAllocatedTypeSourceInfo,
+        ToSourceRange, ToDirectInitRange);
+  else
+    return CXXNewExpr::Create(
+        Importer.getToContext(), E->isGlobalNew(), ToOperatorNew,
+        ToOperatorDelete, E->passAlignment(), E->doesUsualArrayDeleteWantSize(),
+        ToPlacementArgs, ToTypeIdParens, ToArraySize, E->getInitializationStyle(),
+        ToInitializer, ToType, ToAllocatedTypeSourceInfo, ToSourceRange,
+        ToDirectInitRange);
 }
 
 ExpectedStmt ASTNodeImporter::VisitCXXDeleteExpr(CXXDeleteExpr *E) {
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 83af7998f683382..7c7f4c2f31542b9 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -207,6 +207,7 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
   bool IsParenTypeId = TypeIdParens.isValid();
   CXXNewExprBits.IsParenTypeId = IsParenTypeId;
   CXXNewExprBits.NumPlacementArgs = PlacementArgs.size();
+  CXXNewExprBits.IsPlacementNewExpr = false;
 
   if (ArraySize)
     getTrailingObjects<Stmt *>()[arraySizeOffset()] = *ArraySize;
@@ -234,6 +235,18 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
   setDependence(computeDependence(this));
 }
 
+CXXNewExpr::CXXNewExpr(Expr *PlacementArg,
+                       SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
+                       CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
+                       QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
+                       SourceRange DirectInitRange)
+    : CXXNewExpr(true, nullptr, nullptr, false, false,
+                 ArrayRef<Expr *>(&PlacementArg, 1), TypeIdParens, ArraySize,
+                 InitializationStyle, Initializer, Ty,
+                 AllocatedTypeInfo, Range, DirectInitRange) {
+    CXXNewExprBits.IsPlacementNewExpr = true;
+}
+
 CXXNewExpr::CXXNewExpr(EmptyShell Empty, bool IsArray,
                        unsigned NumPlacementArgs, bool IsParenTypeId)
     : Expr(CXXNewExprClass, Empty) {
@@ -265,6 +278,25 @@ CXXNewExpr *CXXNewExpr::Create(
                  AllocatedTypeInfo, Range, DirectInitRange);
 }
 
+CXXNewExpr *CXXNewExpr::CreatePlacementNew(
+    const ASTContext &Ctx, Expr *PlacementArg,
+    SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
+    CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
+    QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
+    SourceRange DirectInitRange) {
+  bool IsArray = ArraySize.has_value();
+  bool HasInit = Initializer != nullptr;
+  bool IsParenTypeId = TypeIdParens.isValid();
+  void *Mem =
+      Ctx.Allocate(totalSizeToAlloc<Stmt *, SourceRange>(
+                       IsArray + HasInit + 1, IsParenTypeId),
+                   alignof(CXXNewExpr));
+  return new (Mem)
+      CXXNewExpr(PlacementArg, TypeIdParens,
+                 ArraySize, InitializationStyle, Initializer, Ty,
+                 AllocatedTypeInfo, Range, DirectInitRange);
+}
+
 CXXNewExpr *CXXNewExpr::CreateEmpty(const ASTContext &Ctx, bool IsArray,
                                     bool HasInit, unsigned NumPlacementArgs,
                                     bool IsParenTypeId) {
@@ -277,6 +309,8 @@ CXXNewExpr *CXXNewExpr::CreateEmpty(const ASTContext &Ctx, bool IsArray,
 }
 
 bool CXXNewExpr::shouldNullCheckAllocation() const {
+  if (isPlacementNewExpr())
+    return false;
   if (getOperatorNew()->getLangOpts().CheckNew)
     return true;
   return !getOperatorNew()->hasAttr<ReturnsNonNullAttr>() &&
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index e16fec6109e744e..7652e22a6a51b4f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9740,7 +9740,7 @@ static bool EvaluateArrayNewConstructExpr(EvalInfo &Info, LValue &This,
                                           QualType AllocType);
 
 bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
-  if (!Info.getLangOpts().CPlusPlus20)
+  if (!Info.getLangOpts().CPlusPlus20 && !E->isPlacementNewExpr())
     Info.CCEDiag(E, diag::note_constexpr_new);
 
   // We cannot speculatively evaluate a delete expression.
@@ -9751,8 +9751,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
 
   bool IsNothrow = false;
   bool IsPlacement = false;
-  if (OperatorNew->isReservedGlobalPlacementOperator() &&
-      Info.CurrentCall->isStdFunction() && !E->isArray()) {
+  if (E->isReservedPlacementNew() && Info.CurrentCall->isStdFunction() && !E->isArray()) {
     // FIXME Support array placement new.
     assert(E->getNumPlacementArgs() == 1);
     if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
@@ -9760,7 +9759,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
     if (Result.Designator.Invalid)
       return false;
     IsPlacement = true;
-  } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
+  } else if (E->isPlacementNewExpr() || !OperatorNew->isReplaceableGlobalAllocationFunction()) {
     Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
         << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
     return false;
diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index ace5178bf625828..82c4f5e64416e21 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1350,6 +1350,7 @@ void JSONNodeDumper::VisitCXXNewExpr(const CXXNewExpr *NE) {
   attributeOnlyIfTrue("isGlobal", NE->isGlobalNew());
   attributeOnlyIfTrue("isArray", NE->isArray());
   attributeOnlyIfTrue("isPlacement", NE->getNumPlacementArgs() != 0);
+  attributeOnlyIfTrue("isPlacementNewExpr", NE->isPlacementNewExpr());
   switch (NE->getInitializationStyle()) {
   case CXXNewInitializationStyle::None:
   case CXXNewInitializationStyle::Implicit:
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index ab4a013de5f552c..ce3d9b64de76ab0 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2271,7 +2271,10 @@ void StmtPrinter::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *Node) {
 void StmtPrinter::VisitCXXNewExpr(CXXNewExpr *E) {
   if (E->isGlobalNew())
     OS << "::";
-  OS << "new ";
+  if (E->isPlacementNewExpr())
+    OS << "_placement_new ";
+  else
+    OS << "new ";
   unsigned NumPlace = E->getNumPlacementArgs();
   if (NumPlace > 0 && !isa<CXXDefaultArgExpr>(E->getPlacementArg(0))) {
     OS << "(";
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 8128219dd6f63c9..c3590f50eebfd2e 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2096,6 +2096,7 @@ void StmtProfiler::VisitCXXNewExpr(const CXXNewExpr *S) {
   ID.AddInteger(S->getNumPlacementArgs());
   ID.AddBoolean(S->isGlobalNew());
   ID.AddBoolean(S->isParenTypeId());
+  ID.AddBoolean(S->isPlacementNewExpr());
   ID.AddInteger(llvm::to_underlying(S->getInitializationStyle()));
 }
 
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index e8274fcd5cfe9cb..57b6b9087826355 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1356,6 +1356,8 @@ void TextNodeDumper::VisitCXXNewExpr(const CXXNewExpr *Node) {
     OS << " global";
   if (Node->isArray())
     OS << " array";
+  if (Node->isPlacementNewExpr())
+    OS << " builtin placement-new expression";
   if (Node->getOperatorNew()) {
     OS << ' ';
     dumpBareDeclRef(Node->getOperatorNew());
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 98ae56e2df88184..cfc16f2ffd3ad04 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -693,7 +693,7 @@ static CharUnits CalculateCookiePadding(CodeGenFunction &CGF,
 
   // No cookie is required if the operator new[] being used is the
   // reserved placement operator new[].
-  if (E->getOperatorNew()->isReservedGlobalPlacementOperator())
+  if (E->isReservedPlacementNew())
     return CharUnits::Zero();
 
   return CGF.CGM.getCXXABI().GetArrayCookieSize(E);
@@ -1584,7 +1584,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
   // operator, just "inline" it directly.
   Address allocation = Address::invalid();
   CallArgList allocatorArgs;
-  if (allocator->isReservedGlobalPlacementOperator()) {
+  if (E->isReservedPlacementNew()) {
     assert(E->getNumPlacementArgs() == 1);
     const Expr *arg = *E->placement_arguments().begin();
 
@@ -1599,6 +1599,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
 
     // Set up allocatorArgs for the call to operator delete if it's not
     // the reserved global operator.
+    assert(!E->isPlacementNewExpr() || !E->getOperatorDelete());
     if (E->getOperatorDelete() &&
         !E->getOperatorDelete()->isReservedGlobalPlacementOperator()) {
       allocatorArgs.add(RValue::get(allocSize), getContext().getSizeType());
@@ -1724,8 +1725,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
   // vptrs information which may be included in previous type.
   // To not break LTO with different optimizations levels, we do it regardless
   // of optimization level.
-  if (CGM.getCodeGenOpts().StrictVTablePointers &&
-      allocator->isReservedGlobalPlacementOperator())
+  if (CGM.getCodeGenOpts().StrictVTablePointers && E->isReservedPlacementNew())
     result = Builder.CreateLaunderInvariantGroup(result);
 
   // Emit sanitizer checks for pointer value now, so that in the case of an
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 4c7f516e308ca00..d0ed0289bcce52a 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3837,7 +3837,10 @@ namespace {
     }
 
     bool VisitCXXNewExpr(CXXNewExpr *E) {
-      SafeToInline = E->getOperatorNew()->hasAttr<DLLImportAttr>();
+      if (E->isPlacementNewExpr())
+        SafeToInline = true;
+      else
+        SafeToInline = E->getOperatorNew()->hasAttr<DLLImportAttr>();
       return SafeToInline;
     }
   };
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 7968c62cbd3e7b3..93ba4a060342a22 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -606,8 +606,8 @@ class RuntimeInterfaceBuilder
       Expr *Args[] = {AllocCall.get()};
       ExprResult CXXNewCall = S.BuildCXXNew(
           E->getSourceRange(),
-          /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
-          /*PlacementRParen=*/SourceLocation(),
+          /*UseGlobal=*/true, /*IsPlacementNewExpr=*/false,
+          /*PlacementLParen=*/SourceLocation(), Args, /*PlacementRParen=*/SourceLocation(),
           /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
           E->getSourceRange(), E);
 
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 78c3ab72979a007..18b2fd86e566e5c 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5457,7 +5457,8 @@ bool Parser::isTypeSpecifierQualifier() {
 
   case tok::coloncolon:   // ::foo::bar
     if (NextToken().is(tok::kw_new) ||    // ::new
-        NextToken().is(tok::kw_delete))   // ::delete
+        NextToken().is(tok::kw_delete) || // ::delete
+        NextToken().is(tok::kw__placement_new)) // ::_placement_new
       return false;
 
     if (TryAnnotateTypeOrScopeToken())
@@ -5650,7 +5651,8 @@ bool Parser::isDeclarationSpecifier(
     if (!getLangOpts().CPlusPlus)
       return false;
     if (NextToken().is(tok::kw_new) ||    // ::new
-        NextToken().is(tok::kw_delete))   // ::delete
+        NextToken().is(tok::kw_delete) || // ::delete
+        NextToken().is(tok::kw__placement_new)) // ::_placement_new
       return false;
 
     // Annotate typenames and C++ scope specifiers.  If we get one, just
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 53fba3b2f59242b..9b2e03221d5d407 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1682,7 +1682,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
     // ::new -> [C++] new-expression
     // ::delete -> [C++] delete-expression
     SourceLocation CCLoc = ConsumeToken();
-    if (Tok.is(tok::kw_new)) {
+    if (Tok.is(tok::kw_new) || Tok.is(tok::kw__placement_new)) {
       if (NotPrimaryExpression)
         *NotPrimaryExpression = true;
       Res = ParseCXXNewExpression(true, CCLoc);
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 99b4931004546c1..f21680834cfc531 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -186,7 +186,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
   if (Tok.is(tok::coloncolon)) {
     // ::new and ::delete aren't nested-name-specifiers.
     tok::TokenKind NextKind = NextToken().getKind();
-    if (NextKind == tok::kw_new || NextKind == tok::kw_delete)
+    if (NextKind == tok::kw_new || NextKind == tok::kw_delete || NextKind == tok::kw__placement_new)
       return false;
 
     if (NextKind == tok::l_brace) {
@@ -3171,7 +3171,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
 ///
 ExprResult
 Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
-  assert(Tok.is(tok::kw_new) && "expected 'new' token");
+  assert((Tok.is(tok::kw_new) || Tok.is(tok::kw__placement_new)) && "expected 'new' token");
+  const bool IsPlacementNewExpr = Tok.is(tok::kw__placement_new);
   ConsumeToken();   // Consume 'new'
 
   // A '(' now can be a new-placement or the '(' wrapping the type-id in the
@@ -3201,6 +3202,14 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
       return ExprError();
     }
 
+    if (IsPlacementNewExpr) {
+      if (PlacementArgs.size() != 1) {
+        Diag(PlacementLParen, diag::err_placement_new_expected_one_argument);
+        SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
+        return ExprError();
+      }
+    }
+
     if (PlacementArgs.empty()) {
       // Reset the placement locations. There was no placement.
       TypeIdParens = T.getRange();
@@ -3227,6 +3236,10 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
         }
       }
     }
+  } else if (IsPlacementNewExpr) {
+    Diag(Tok, diag::err_expected_lparen_after) << "::_placement_new";
+    SkipUntil(tok::semi, StopAtSemi | StopBeforeMatch);
+    return ExprError();
   } else {
     // A new-type-id is a simplified type-id, where essentially the
     // direct-declarator is replaced by a direct-new-declarator.
@@ -3295,7 +3308,7 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
   if (Initializer.isInvalid())
     return Initializer;
 
-  return Actions.ActOnCXXNew(Start, UseGlobal, PlacementLParen,
+  return Actions.ActOnCXXNew(Start, UseGlobal, IsPlacementNewExpr, PlacementLParen,
                              PlacementArgs, PlacementRParen,
                              TypeIdParens, DeclaratorInfo, Initializer.get());
 }
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp
index c0261c462b8834e..10764caf645dbea 100644
--- a/clang/lib/Parse/ParseObjc.cpp
+++ b/clang/lib/Parse/ParseObjc.cpp
@@ -1120,6 +1120,7 @@ IdentifierInfo *Parser::ParseObjCSelectorPiece(SourceLocation &SelectorLoc) {
   case tok::kw_mutable:
   case tok::kw_namespace:
   case tok::kw_new:
+  case tok::kw__placement_new:
   case tok::kw_operator:
   case tok::kw_private:
   case tok::kw_protected:
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 28decc4fc43f9b8..623e3eb4ed70316 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1445,7 +1445,8 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
   case tok::coloncolon: {    // ::foo::bar
     const Token &Next = NextToken();
     if (Next.isOneOf(tok::kw_new,       // ::new
-                     tok::kw_delete))   // ::delete
+                     tok::kw_delete,    // ::delete
+                     tok::kw__placement_new)) // ::_placement_new
       return TPResult::False;
     [[fallthrough]];
   }
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 023411c7edc946b..b8e606b3a45b965 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1875,7 +1875,7 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
 /// \param Initializer The initializing expression or initializer-list, or null
 ///   if there is none.
 ExprResult
-Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
+Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, bool IsPlacementNewExpr,
                   SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
                   SourceLocation PlacementRParen, SourceRange TypeIdParens,
                   Declarator &D, Expr *Initializer) {
@@ -1940,7 +1940,7 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
   if (ParenListExpr *List = dyn_cast_or_null<ParenListExpr>(Initializer))
     DirectInitRange = List->getSourceRange();
 
-  return BuildCXXNew(SourceRange(StartLoc, D.getEndLoc()), UseGlobal,
+  return BuildCXXNew(SourceRange(StartLoc, D.getEndLoc()), UseGlobal, IsPlacementNewExpr,
                      PlacementLParen, PlacementArgs, PlacementRParen,
                      TypeIdParens, AllocType, TInfo, ArraySize, DirectInitRange,
                      Initializer);
@@ -1997,7 +1997,7 @@ void Sema::diagnoseUnavailableAlignedAllocation(const FunctionDecl &FD,
   }
 }
 
-ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
+ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacementNewExpr,
                              SourceLocation PlacementLParen,
                              MultiExprArg PlacementArgs,
                              SourceLocation PlacementRParen,
@@ -2286,9 +2286,10 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
   bool PassAlignment = getLangOpts().AlignedAllocation &&
                        Alignment > NewAlignment;
 
+  bool HaveDependentPlacementTypes = AllocType->isDependentType() || Expr::hasAnyTypeDependentArguments(PlacementArgs);
+
   AllocationFunctionScope Scope = UseGlobal ? AFS_Global : AFS_Both;
-  if (!AllocType->isDependentType() &&
-      !Expr::hasAnyTypeDependentArguments(PlacementArgs) &&
+  if (!HaveDependentPlacementTypes && !IsPlacementNewExpr &&
       FindAllocationFunctions(
           StartLoc, SourceRange(PlacementLParen, PlacementRParen), Scope, Scope,
           AllocType, ArraySize.has_value(), PassAlignment, PlacementArgs,
@@ -2298,15 +2299,29 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
   // If this is an array allocation, compute whether the usual array
   // deallocation function for the type has a size_t parameter.
   bool UsualArrayDeleteWantsSize = false;
-  if (ArraySize && !AllocType->isDependentType())
+  if (!IsPlacementNewExpr && ArraySize && !AllocType->isDependentType())
     UsualArrayDeleteWantsSize =
         doesUsualArrayDeleteWantSize(*this, StartLoc, AllocType);
 
-  SmallVector<Expr *, 8> AllPlaceArgs;
-  if (OperatorNew) {
+  if (IsPlacementNewExpr && !HaveDependentPlacementTypes) {
+    assert(PlacementArgs.size() == 1);
+    assert(UseGlobal);
+    QualType VoidPtr = Context.getPointerType(Context.VoidTy);
+
+    InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, VoidPtr, false);
+    ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), PlacementArgs[0], false, false);
+    if (ArgE.isInvalid())
+        return ExprError();
+
+    Expr *Arg = ArgE.getAs<Expr>();
+    CheckArrayAccess(Arg);
+
+    PlacementArgs[0] = Arg;
+  } else if (OperatorNew) {
     auto *Proto = OperatorNew->getType()->castAs<FunctionProtoType>();
     VariadicCallType CallType = Proto->isVariadic() ? VariadicFunction
                                                     : VariadicDoesNotApply;
+    SmallVector<Expr *, 8> AllPlaceArgs;
 
     // We've already converted the placement args, just fill in any default
     // arguments. Skip the first parameter because we don't have a corresponding
@@ -2471,21 +2486,28 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
 
   // Mark the new and delete operators as referenced.
   if (OperatorNew) {
+    assert(!IsPlacementNewExpr);
     if (DiagnoseUseOfDecl(OperatorNew, StartLoc))
       return ExprError();
     MarkFunctionReferenced(StartLoc, OperatorNew);
   }
   if (OperatorDelete) {
+    assert(!IsPlacementNewExpr);
     if (DiagnoseUseOfDecl(OperatorDelete, StartLoc))
       return ExprError();
     MarkFunctionReferenced(StartLoc, OperatorDelete);
   }
 
-  return CXXNewExpr::Create(Context, UseGlobal, OperatorNew, OperatorDelete,
-                            PassAlignment, UsualArrayDeleteWantsSize,
-                            PlacementArgs, TypeIdParens, ArraySize, InitStyle,
-                            Initializer, ResultType, AllocTypeInfo, Range,
-                            DirectInitRange);
+  if (IsPlacementNewExpr)
+    return CXXNewExpr::CreatePlacementNew(Context, PlacementArgs[0], TypeIdParens, ArraySize,
+                                          InitStyle, Initializer, ResultType, AllocTypeInfo,
+                                          Range, DirectInitRange);
+  else
+    return CXXNewExpr::Create(Context, UseGlobal, OperatorNew, OperatorDelete,
+                              PassAlignment, UsualArrayDeleteWantsSize,
+                              PlacementArgs, TypeIdParens, ArraySize, InitStyle,
+                              Initializer, ResultType, AllocTypeInfo, Range,
+                              DirectInitRange);
 }
 
 /// Checks that a type is suitable as the allocated type
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e24f710fdedd4e2..5fc64fa50dd431b 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3343,7 +3343,7 @@ class TreeTransform {
   ///
   /// By default, performs semantic analysis to build the new expression.
   /// Subclasses may override this routine to provide different behavior.
-  ExprResult RebuildCXXNewExpr(SourceLocation StartLoc, bool UseGlobal,
+  ExprResult RebuildCXXNewExpr(SourceLocation StartLoc, bool UseGlobal, bool IsPlacementNewExpr,
                                SourceLocation PlacementLParen,
                                MultiExprArg PlacementArgs,
                                SourceLocation PlacementRParen,
@@ -3351,7 +3351,7 @@ class TreeTransform {
                                TypeSourceInfo *AllocatedTypeInfo,
                                std::optional<Expr *> ArraySize,
                                SourceRange DirectInitRange, Expr *Initializer) {
-    return getSema().BuildCXXNew(StartLoc, UseGlobal,
+    return getSema().BuildCXXNew(StartLoc, UseGlobal, IsPlacementNewExpr,
                                  PlacementLParen,
                                  PlacementArgs,
                                  PlacementRParen,
@@ -12539,7 +12539,7 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) {
   }
 
   return getDerived().RebuildCXXNewExpr(
-      E->getBeginLoc(), E->isGlobalNew(),
+      E->getBeginLoc(), E->isGlobalNew(), E->isPlacementNewExpr(),
       /*FIXME:*/ E->getBeginLoc(), PlacementArgs,
       /*FIXME:*/ E->getBeginLoc(), E->getTypeIdParens(), AllocType,
       AllocTypeInfo, ArraySize, E->getDirectInitRange(), NewInit.get());
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index d7d0c0e5bb21b47..7f55e308fa1bb08 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1874,6 +1874,8 @@ void ASTStmtReader::VisitCXXNewExpr(CXXNewExpr *E) {
   E->CXXNewExprBits.UsualArrayDeleteWantsSize = Record.readInt();
   E->CXXNewExprBits.StoredInitializationStyle = Record.readInt();
 
+  E->CXXNewExprBits.IsPlacementNewExpr = Record.readInt();
+
   assert((IsArray == E->isArray()) && "Wrong IsArray!");
   assert((HasInit == E->hasInitializer()) && "Wrong HasInit!");
   assert((NumPlacementArgs == E->getNumPlacementArgs()) &&
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 59be6828fafabf6..2fb5f40e6f448ac 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1799,6 +1799,8 @@ void ASTStmtWriter::VisitCXXNewExpr(CXXNewExpr *E) {
   Record.push_back(E->doesUsualArrayDeleteWantSize());
   Record.push_back(E->CXXNewExprBits.StoredInitializationStyle);
 
+  Record.push_back(E->isPlacementNewExpr());
+
   Record.AddDeclRef(E->getOperatorNew());
   Record.AddDeclRef(E->getOperatorDelete());
   Record.AddTypeSourceInfo(E->getAllocatedTypeSourceInfo());
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
index 99e11a15c08dc2c..e1bdcae0377e7ef 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
@@ -295,7 +295,7 @@ bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
 void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
                                        CheckerContext &C) const {
   // Check only the default placement new.
-  if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
+  if (!NE->isReservedPlacementNew())
     return;
 
   if (NE->getNumPlacementArgs() == 0)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 504fd7f05e0f99b..339b26d44cc8ad0 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1004,8 +1004,9 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
   SVal symVal = UnknownVal();
   FunctionDecl *FD = CNE->getOperatorNew();
 
-  bool IsStandardGlobalOpNewFunction =
-      FD->isReplaceableGlobalAllocationFunction();
+  bool IsStandardGlobalOpNewFunction = false;
+  if (FD)
+    IsStandardGlobalOpNewFunction = FD->isReplaceableGlobalAllocationFunction();
 
   ProgramStateRef State = Pred->getState();
 
@@ -1046,7 +1047,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
     // where new can return NULL. If we end up supporting that option, we can
     // consider adding a check for it here.
     // C++11 [basic.stc.dynamic.allocation]p3.
-    if (const auto *ProtoType = FD->getType()->getAs<FunctionProtoType>())
+    if (const auto *ProtoType = FD ? FD->getType()->getAs<FunctionProtoType>() : nullptr)
       if (!ProtoType->isNothrow())
         if (auto dSymVal = symVal.getAs<DefinedOrUnknownSVal>())
           State = State->assume(*dSymVal, true);
@@ -1099,7 +1100,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
   // CXXNewExpr, we need to make sure that the constructed object is not
   // immediately invalidated here. (The placement call should happen before
   // the constructor call anyway.)
-  if (FD->isReservedGlobalPlacementOperator()) {
+  if (CNE->isReservedPlacementNew()) {
     // Non-array placement new should always return the placement location.
     SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx);
     Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(),
diff --git a/clang/test/AST/ast-dump-expr.cpp b/clang/test/AST/ast-dump-expr.cpp
index 69e65e22d61d0d0..7fddd913786cc1e 100644
--- a/clang/test/AST/ast-dump-expr.cpp
+++ b/clang/test/AST/ast-dump-expr.cpp
@@ -155,6 +155,12 @@ void UnaryExpressions(int *p) {
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:8> 'int *' lvalue ParmVar 0x{{[^ ]*}} 'p' 'int *'
 
+  ::_placement_new (p) int;
+  // CHECK: CXXNewExpr 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:24> 'int *' global builtin placement-new expression
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void *' <BitCast>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int *' <LValueToRValue>
+  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:21> 'int *' lvalue ParmVar 0x{{[^ ]*}} 'p' 'int *'
+
   new (p) int{12};
   // CHECK: CXXNewExpr 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:17> 'int *' Function 0x{{[^ ]*}} 'operator new' 'void *(std::size_t, void *)'
   // CHECK-NEXT: InitListExpr 0x{{[^ ]*}} <col:14, col:17> 'int'
@@ -163,6 +169,14 @@ void UnaryExpressions(int *p) {
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int *' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:8> 'int *' lvalue ParmVar 0x{{[^ ]*}} 'p' 'int *'
 
+  ::_placement_new (p) int{12};
+  // CHECK: CXXNewExpr 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:30> 'int *' global builtin placement-new expression
+  // CHECK-NEXT: InitListExpr 0x{{[^ ]*}} <col:27, col:30> 'int'
+  // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} <col:28> 'int' 12
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void *' <BitCast>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int *' <LValueToRValue>
+  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:21> 'int *' lvalue ParmVar 0x{{[^ ]*}} 'p' 'int *'
+
   ::delete p;
   // CHECK: CXXDeleteExpr 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:12> 'void' global Function 0x{{[^ ]*}} 'operator delete' 'void (void *) noexcept'
   // CHECK-NEXT: ImplicitCastExpr
diff --git a/clang/test/CodeGenCXX/new.cpp b/clang/test/CodeGenCXX/new.cpp
index e278d9acfe9ee23..7cd3232a295e0a1 100644
--- a/clang/test/CodeGenCXX/new.cpp
+++ b/clang/test/CodeGenCXX/new.cpp
@@ -219,6 +219,15 @@ namespace test15 {
     new (p, true) A();
   }
 
+  // CHECK-LABEL:    define{{.*}} void @_ZN6test156test0cEPv(
+  // CHECK:      [[P:%.*]] = load ptr, ptr
+  // CHECK-NOT:  icmp eq ptr [[P]], null
+  // CHECK-NOT:  br i1
+  // CHECK-NEXT: call void @_ZN6test151AC1Ev(ptr {{[^,]*}} [[P]])
+  void test0c(void *p) {
+    ::_placement_new (p) A();
+  }
+
   // CHECK-LABEL:    define{{.*}} void @_ZN6test156test1aEPv(
   // CHECK:      [[P:%.*]] = load ptr, ptr
   // CHECK-NOT:  icmp eq ptr [[P]], null
@@ -251,9 +260,24 @@ namespace test15 {
     new (p, true) A[5];
   }
 
+  // CHECK-LABEL:    define{{.*}} void @_ZN6test156test1cEPv(
+  // CHECK:      [[P:%.*]] = load ptr, ptr
+  // CHECK-NOT:  icmp eq ptr [[P]], null
+  // CHECK-NOT:  br i1
+  // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A:.*]], ptr [[P]], i64 5
+  // CHECK-NEXT: br label
+  // CHECK:      [[CUR:%.*]] = phi ptr [ [[P]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ]
+  // CHECK-NEXT: call void @_ZN6test151AC1Ev(ptr {{[^,]*}} [[CUR]])
+  // CHECK-NEXT: [[NEXT]] = getelementptr inbounds [[A]], ptr [[CUR]], i64 1
+  // CHECK-NEXT: [[DONE:%.*]] = icmp eq ptr [[NEXT]], [[END]]
+  // CHECK-NEXT: br i1 [[DONE]]
+  void test1c(void *p) {
+    ::_placement_new (p) A[5];
+  }
+
   // TODO: it's okay if all these size calculations get dropped.
   // FIXME: maybe we should try to throw on overflow?
-  // CHECK-LABEL:    define{{.*}} void @_ZN6test155test2EPvi(
+  // CHECK-LABEL:    define{{.*}} void @_ZN6test156test2aEPvi(
   // CHECK:      [[N:%.*]] = load i32, ptr
   // CHECK-NEXT: [[T0:%.*]] = sext i32 [[N]] to i64
   // CHECK-NEXT: [[P:%.*]] = load ptr, ptr
@@ -263,9 +287,25 @@ namespace test15 {
   // CHECK-NEXT: br label
   // CHECK:      [[CUR:%.*]] = phi ptr [ [[P]],
   // CHECK-NEXT: call void @_ZN6test151AC1Ev(ptr {{[^,]*}} [[CUR]])
-  void test2(void *p, int n) {
+  void test2a(void *p, int n) {
     new (p) A[n];
   }
+
+  // TODO: it's okay if all these size calculations get dropped.
+  // FIXME: maybe we should try to throw on overflow?
+  // CHECK-LABEL:    define{{.*}} void @_ZN6test156test2bEPvi(
+  // CHECK:      [[N:%.*]] = load i32, ptr
+  // CHECK-NEXT: [[T0:%.*]] = sext i32 [[N]] to i64
+  // CHECK-NEXT: [[P:%.*]] = load ptr, ptr
+  // CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq i64 [[T0]], 0
+  // CHECK-NEXT: br i1 [[ISEMPTY]],
+  // CHECK:      [[END:%.*]] = getelementptr inbounds [[A]], ptr [[P]], i64 [[T0]]
+  // CHECK-NEXT: br label
+  // CHECK:      [[CUR:%.*]] = phi ptr [ [[P]],
+  // CHECK-NEXT: call void @_ZN6test151AC1Ev(ptr {{[^,]*}} [[CUR]])
+  void test2b(void *p, int n) {
+    ::_placement_new (p) A[n];
+  }
 }
 
 namespace PR10197 {
diff --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp
index 0270e42b7389fec..20871c6ebc9bdf0 100644
--- a/clang/test/SemaCXX/new-delete.cpp
+++ b/clang/test/SemaCXX/new-delete.cpp
@@ -58,6 +58,9 @@ void good_news()
   U *pu = new (ps) U;
   V *pv = new (ps) V;
 
+  U *pu2 = ::_placement_new ((S*)0) U;
+  int *pi2 = ::_placement_new ((void*)0) int;
+
   pi = new (S(1.0f, 2)) int;
 
   (void)new int[true];
@@ -87,6 +90,10 @@ void bad_news(int *ip)
   (void)new; // expected-error {{expected a type}}
   (void)new 4; // expected-error {{expected a type}}
   (void)new () int; // expected-error {{expected expression}}
+  (void)::_placement_new () int; // expected-error {{expected expression}}
+  (void)_placement_new (ip) int; // expected-error {{expected expression}}
+  (void)::_placement_new int; // expected-error {{expected '(' after '::_placement_new'}}
+  (void)::_placement_new (ip, ip) int; // expected-error {{expected only one argument in placement params}}
   (void)new int[1.1];
 #if __cplusplus <= 199711L
   // expected-error at -2 {{array size expression must have integral or enumeration type, not 'double'}}

>From 2d2e463e10f27f9be21d6cad998e15923ca5f676 Mon Sep 17 00:00:00 2001
From: MaxEW707 <82551778+MaxEW707 at users.noreply.github.com>
Date: Tue, 14 Nov 2023 19:15:25 -0500
Subject: [PATCH 2/4] fix formatting errors

---
 clang/include/clang/AST/ExprCXX.h             |  9 ++--
 clang/include/clang/Sema/Sema.h               | 22 ++++-----
 clang/lib/AST/ASTImporter.cpp                 | 12 ++---
 clang/lib/AST/ExprCXX.cpp                     | 31 ++++++------
 clang/lib/AST/ExprConstant.cpp                |  6 ++-
 clang/lib/Interpreter/Interpreter.cpp         | 13 ++---
 clang/lib/Parse/ParseDecl.cpp                 |  8 +--
 clang/lib/Parse/ParseExprCXX.cpp              | 10 ++--
 clang/lib/Parse/ParseTentative.cpp            |  4 +-
 clang/lib/Sema/SemaExprCXX.cpp                | 49 ++++++++++---------
 clang/lib/Sema/TreeTransform.h                | 17 +++----
 .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp |  3 +-
 12 files changed, 94 insertions(+), 90 deletions(-)

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index d760af796aea28f..b769ddb69c613b5 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2283,8 +2283,8 @@ class CXXNewExpr final
              SourceRange DirectInitRange);
 
   /// Build a c++ builtin placement new expression
-  CXXNewExpr(Expr *PlacementArg,
-             SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
+  CXXNewExpr(Expr *PlacementArg, SourceRange TypeIdParens,
+             std::optional<Expr *> ArraySize,
              CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
              QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
              SourceRange DirectInitRange);
@@ -2308,8 +2308,9 @@ class CXXNewExpr final
   static CXXNewExpr *
   CreatePlacementNew(const ASTContext &Ctx, Expr *PlacementArg,
                      SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
-                     CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
-                     QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
+                     CXXNewInitializationStyle InitializationStyle,
+                     Expr *Initializer, QualType Ty,
+                     TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
                      SourceRange DirectInitRange);
 
   /// Create an empty c++ new expression.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ab9a0d0713435b6..ec1d86f7ead5694 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6810,18 +6810,18 @@ class Sema final {
                                        bool ListInitialization);
 
   /// ActOnCXXNew - Parsed a C++ 'new' expression.
-  ExprResult ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, bool IsPlacementNewExpr,
-                         SourceLocation PlacementLParen,
-                         MultiExprArg PlacementArgs,
-                         SourceLocation PlacementRParen,
-                         SourceRange TypeIdParens, Declarator &D,
-                         Expr *Initializer);
   ExprResult
-  BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacementNewExpr, SourceLocation PlacementLParen,
-              MultiExprArg PlacementArgs, SourceLocation PlacementRParen,
-              SourceRange TypeIdParens, QualType AllocType,
-              TypeSourceInfo *AllocTypeInfo, std::optional<Expr *> ArraySize,
-              SourceRange DirectInitRange, Expr *Initializer);
+  ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, bool IsPlacementNewExpr,
+              SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
+              SourceLocation PlacementRParen, SourceRange TypeIdParens,
+              Declarator &D, Expr *Initializer);
+  ExprResult
+  BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacementNewExpr,
+              SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
+              SourceLocation PlacementRParen, SourceRange TypeIdParens,
+              QualType AllocType, TypeSourceInfo *AllocTypeInfo,
+              std::optional<Expr *> ArraySize, SourceRange DirectInitRange,
+              Expr *Initializer);
 
   /// Determine whether \p FD is an aligned allocation or deallocation
   /// function that is unavailable.
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 3204fb69b3440f7..625d86b84ef0b26 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8104,16 +8104,16 @@ ExpectedStmt ASTNodeImporter::VisitCXXNewExpr(CXXNewExpr *E) {
 
   if (E->isPlacementNewExpr())
     return CXXNewExpr::CreatePlacementNew(
-        Importer.getToContext(), ToPlacementArgs[0], ToTypeIdParens, ToArraySize,
-        E->getInitializationStyle(), ToInitializer, ToType, ToAllocatedTypeSourceInfo,
-        ToSourceRange, ToDirectInitRange);
+        Importer.getToContext(), ToPlacementArgs[0], ToTypeIdParens,
+        ToArraySize, E->getInitializationStyle(), ToInitializer, ToType,
+        ToAllocatedTypeSourceInfo, ToSourceRange, ToDirectInitRange);
   else
     return CXXNewExpr::Create(
         Importer.getToContext(), E->isGlobalNew(), ToOperatorNew,
         ToOperatorDelete, E->passAlignment(), E->doesUsualArrayDeleteWantSize(),
-        ToPlacementArgs, ToTypeIdParens, ToArraySize, E->getInitializationStyle(),
-        ToInitializer, ToType, ToAllocatedTypeSourceInfo, ToSourceRange,
-        ToDirectInitRange);
+        ToPlacementArgs, ToTypeIdParens, ToArraySize,
+        E->getInitializationStyle(), ToInitializer, ToType,
+        ToAllocatedTypeSourceInfo, ToSourceRange, ToDirectInitRange);
 }
 
 ExpectedStmt ASTNodeImporter::VisitCXXDeleteExpr(CXXDeleteExpr *E) {
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 7c7f4c2f31542b9..e47ddc4ab083ce8 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -235,16 +235,17 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
   setDependence(computeDependence(this));
 }
 
-CXXNewExpr::CXXNewExpr(Expr *PlacementArg,
-                       SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
-                       CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
-                       QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
+CXXNewExpr::CXXNewExpr(Expr *PlacementArg, SourceRange TypeIdParens,
+                       std::optional<Expr *> ArraySize,
+                       CXXNewInitializationStyle InitializationStyle,
+                       Expr *Initializer, QualType Ty,
+                       TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
                        SourceRange DirectInitRange)
     : CXXNewExpr(true, nullptr, nullptr, false, false,
                  ArrayRef<Expr *>(&PlacementArg, 1), TypeIdParens, ArraySize,
-                 InitializationStyle, Initializer, Ty,
-                 AllocatedTypeInfo, Range, DirectInitRange) {
-    CXXNewExprBits.IsPlacementNewExpr = true;
+                 InitializationStyle, Initializer, Ty, AllocatedTypeInfo, Range,
+                 DirectInitRange) {
+  CXXNewExprBits.IsPlacementNewExpr = true;
 }
 
 CXXNewExpr::CXXNewExpr(EmptyShell Empty, bool IsArray,
@@ -279,22 +280,20 @@ CXXNewExpr *CXXNewExpr::Create(
 }
 
 CXXNewExpr *CXXNewExpr::CreatePlacementNew(
-    const ASTContext &Ctx, Expr *PlacementArg,
-    SourceRange TypeIdParens, std::optional<Expr *> ArraySize,
+    const ASTContext &Ctx, Expr *PlacementArg, SourceRange TypeIdParens,
+    std::optional<Expr *> ArraySize,
     CXXNewInitializationStyle InitializationStyle, Expr *Initializer,
     QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range,
     SourceRange DirectInitRange) {
   bool IsArray = ArraySize.has_value();
   bool HasInit = Initializer != nullptr;
   bool IsParenTypeId = TypeIdParens.isValid();
-  void *Mem =
-      Ctx.Allocate(totalSizeToAlloc<Stmt *, SourceRange>(
-                       IsArray + HasInit + 1, IsParenTypeId),
-                   alignof(CXXNewExpr));
+  void *Mem = Ctx.Allocate(totalSizeToAlloc<Stmt *, SourceRange>(
+                               IsArray + HasInit + 1, IsParenTypeId),
+                           alignof(CXXNewExpr));
   return new (Mem)
-      CXXNewExpr(PlacementArg, TypeIdParens,
-                 ArraySize, InitializationStyle, Initializer, Ty,
-                 AllocatedTypeInfo, Range, DirectInitRange);
+      CXXNewExpr(PlacementArg, TypeIdParens, ArraySize, InitializationStyle,
+                 Initializer, Ty, AllocatedTypeInfo, Range, DirectInitRange);
 }
 
 CXXNewExpr *CXXNewExpr::CreateEmpty(const ASTContext &Ctx, bool IsArray,
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 7652e22a6a51b4f..8ee1887c6ccf98a 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9751,7 +9751,8 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
 
   bool IsNothrow = false;
   bool IsPlacement = false;
-  if (E->isReservedPlacementNew() && Info.CurrentCall->isStdFunction() && !E->isArray()) {
+  if (E->isReservedPlacementNew() && Info.CurrentCall->isStdFunction() &&
+      !E->isArray()) {
     // FIXME Support array placement new.
     assert(E->getNumPlacementArgs() == 1);
     if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
@@ -9759,7 +9760,8 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
     if (Result.Designator.Invalid)
       return false;
     IsPlacement = true;
-  } else if (E->isPlacementNewExpr() || !OperatorNew->isReplaceableGlobalAllocationFunction()) {
+  } else if (E->isPlacementNewExpr() ||
+             !OperatorNew->isReplaceableGlobalAllocationFunction()) {
     Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
         << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
     return false;
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 93ba4a060342a22..f05f5168119aec6 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -604,12 +604,13 @@ class RuntimeInterfaceBuilder
             SourceLocation(), Args, SourceLocation());
       }
       Expr *Args[] = {AllocCall.get()};
-      ExprResult CXXNewCall = S.BuildCXXNew(
-          E->getSourceRange(),
-          /*UseGlobal=*/true, /*IsPlacementNewExpr=*/false,
-          /*PlacementLParen=*/SourceLocation(), Args, /*PlacementRParen=*/SourceLocation(),
-          /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
-          E->getSourceRange(), E);
+      ExprResult CXXNewCall =
+          S.BuildCXXNew(E->getSourceRange(),
+                        /*UseGlobal=*/true, /*IsPlacementNewExpr=*/false,
+                        /*PlacementLParen=*/SourceLocation(), Args,
+                        /*PlacementRParen=*/SourceLocation(),
+                        /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI,
+                        std::nullopt, E->getSourceRange(), E);
 
       assert(!CXXNewCall.isInvalid() &&
              "Can't create runtime placement new call!");
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 18b2fd86e566e5c..c89dfff16c12dde 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5456,8 +5456,8 @@ bool Parser::isTypeSpecifierQualifier() {
     return isTypeSpecifierQualifier();
 
   case tok::coloncolon:   // ::foo::bar
-    if (NextToken().is(tok::kw_new) ||    // ::new
-        NextToken().is(tok::kw_delete) || // ::delete
+    if (NextToken().is(tok::kw_new) ||          // ::new
+        NextToken().is(tok::kw_delete) ||       // ::delete
         NextToken().is(tok::kw__placement_new)) // ::_placement_new
       return false;
 
@@ -5650,8 +5650,8 @@ bool Parser::isDeclarationSpecifier(
   case tok::coloncolon:   // ::foo::bar
     if (!getLangOpts().CPlusPlus)
       return false;
-    if (NextToken().is(tok::kw_new) ||    // ::new
-        NextToken().is(tok::kw_delete) || // ::delete
+    if (NextToken().is(tok::kw_new) ||          // ::new
+        NextToken().is(tok::kw_delete) ||       // ::delete
         NextToken().is(tok::kw__placement_new)) // ::_placement_new
       return false;
 
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f21680834cfc531..1d3433f2d564dca 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -186,7 +186,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
   if (Tok.is(tok::coloncolon)) {
     // ::new and ::delete aren't nested-name-specifiers.
     tok::TokenKind NextKind = NextToken().getKind();
-    if (NextKind == tok::kw_new || NextKind == tok::kw_delete || NextKind == tok::kw__placement_new)
+    if (NextKind == tok::kw_new || NextKind == tok::kw_delete ||
+        NextKind == tok::kw__placement_new)
       return false;
 
     if (NextKind == tok::l_brace) {
@@ -3171,7 +3172,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
 ///
 ExprResult
 Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
-  assert((Tok.is(tok::kw_new) || Tok.is(tok::kw__placement_new)) && "expected 'new' token");
+  assert((Tok.is(tok::kw_new) || Tok.is(tok::kw__placement_new)) &&
+         "expected 'new' token");
   const bool IsPlacementNewExpr = Tok.is(tok::kw__placement_new);
   ConsumeToken();   // Consume 'new'
 
@@ -3308,8 +3310,8 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
   if (Initializer.isInvalid())
     return Initializer;
 
-  return Actions.ActOnCXXNew(Start, UseGlobal, IsPlacementNewExpr, PlacementLParen,
-                             PlacementArgs, PlacementRParen,
+  return Actions.ActOnCXXNew(Start, UseGlobal, IsPlacementNewExpr,
+                             PlacementLParen, PlacementArgs, PlacementRParen,
                              TypeIdParens, DeclaratorInfo, Initializer.get());
 }
 
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 623e3eb4ed70316..7d7a044a6b161d3 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1444,8 +1444,8 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
 
   case tok::coloncolon: {    // ::foo::bar
     const Token &Next = NextToken();
-    if (Next.isOneOf(tok::kw_new,       // ::new
-                     tok::kw_delete,    // ::delete
+    if (Next.isOneOf(tok::kw_new,             // ::new
+                     tok::kw_delete,          // ::delete
                      tok::kw__placement_new)) // ::_placement_new
       return TPResult::False;
     [[fallthrough]];
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index b8e606b3a45b965..cf015caffae80ca 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1875,10 +1875,10 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
 /// \param Initializer The initializing expression or initializer-list, or null
 ///   if there is none.
 ExprResult
-Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, bool IsPlacementNewExpr,
-                  SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
-                  SourceLocation PlacementRParen, SourceRange TypeIdParens,
-                  Declarator &D, Expr *Initializer) {
+Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
+                  bool IsPlacementNewExpr, SourceLocation PlacementLParen,
+                  MultiExprArg PlacementArgs, SourceLocation PlacementRParen,
+                  SourceRange TypeIdParens, Declarator &D, Expr *Initializer) {
   std::optional<Expr *> ArraySize;
   // If the specified type is an array, unwrap it and save the expression.
   if (D.getNumTypeObjects() > 0 &&
@@ -1940,10 +1940,10 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, bool IsPlacementNewEx
   if (ParenListExpr *List = dyn_cast_or_null<ParenListExpr>(Initializer))
     DirectInitRange = List->getSourceRange();
 
-  return BuildCXXNew(SourceRange(StartLoc, D.getEndLoc()), UseGlobal, IsPlacementNewExpr,
-                     PlacementLParen, PlacementArgs, PlacementRParen,
-                     TypeIdParens, AllocType, TInfo, ArraySize, DirectInitRange,
-                     Initializer);
+  return BuildCXXNew(SourceRange(StartLoc, D.getEndLoc()), UseGlobal,
+                     IsPlacementNewExpr, PlacementLParen, PlacementArgs,
+                     PlacementRParen, TypeIdParens, AllocType, TInfo, ArraySize,
+                     DirectInitRange, Initializer);
 }
 
 static bool isLegalArrayNewInitializer(CXXNewInitializationStyle Style,
@@ -1997,14 +1997,13 @@ void Sema::diagnoseUnavailableAlignedAllocation(const FunctionDecl &FD,
   }
 }
 
-ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacementNewExpr,
-                             SourceLocation PlacementLParen,
-                             MultiExprArg PlacementArgs,
-                             SourceLocation PlacementRParen,
-                             SourceRange TypeIdParens, QualType AllocType,
-                             TypeSourceInfo *AllocTypeInfo,
-                             std::optional<Expr *> ArraySize,
-                             SourceRange DirectInitRange, Expr *Initializer) {
+ExprResult
+Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacementNewExpr,
+                  SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
+                  SourceLocation PlacementRParen, SourceRange TypeIdParens,
+                  QualType AllocType, TypeSourceInfo *AllocTypeInfo,
+                  std::optional<Expr *> ArraySize, SourceRange DirectInitRange,
+                  Expr *Initializer) {
   SourceRange TypeRange = AllocTypeInfo->getTypeLoc().getSourceRange();
   SourceLocation StartLoc = Range.getBegin();
 
@@ -2286,7 +2285,9 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacement
   bool PassAlignment = getLangOpts().AlignedAllocation &&
                        Alignment > NewAlignment;
 
-  bool HaveDependentPlacementTypes = AllocType->isDependentType() || Expr::hasAnyTypeDependentArguments(PlacementArgs);
+  bool HaveDependentPlacementTypes =
+      AllocType->isDependentType() ||
+      Expr::hasAnyTypeDependentArguments(PlacementArgs);
 
   AllocationFunctionScope Scope = UseGlobal ? AFS_Global : AFS_Both;
   if (!HaveDependentPlacementTypes && !IsPlacementNewExpr &&
@@ -2308,10 +2309,12 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacement
     assert(UseGlobal);
     QualType VoidPtr = Context.getPointerType(Context.VoidTy);
 
-    InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, VoidPtr, false);
-    ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), PlacementArgs[0], false, false);
+    InitializedEntity Entity =
+        InitializedEntity::InitializeParameter(Context, VoidPtr, false);
+    ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(),
+                                                PlacementArgs[0], false, false);
     if (ArgE.isInvalid())
-        return ExprError();
+      return ExprError();
 
     Expr *Arg = ArgE.getAs<Expr>();
     CheckArrayAccess(Arg);
@@ -2499,9 +2502,9 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacement
   }
 
   if (IsPlacementNewExpr)
-    return CXXNewExpr::CreatePlacementNew(Context, PlacementArgs[0], TypeIdParens, ArraySize,
-                                          InitStyle, Initializer, ResultType, AllocTypeInfo,
-                                          Range, DirectInitRange);
+    return CXXNewExpr::CreatePlacementNew(
+        Context, PlacementArgs[0], TypeIdParens, ArraySize, InitStyle,
+        Initializer, ResultType, AllocTypeInfo, Range, DirectInitRange);
   else
     return CXXNewExpr::Create(Context, UseGlobal, OperatorNew, OperatorDelete,
                               PassAlignment, UsualArrayDeleteWantsSize,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 5fc64fa50dd431b..0c6abd7cad1b9ee 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3343,7 +3343,8 @@ class TreeTransform {
   ///
   /// By default, performs semantic analysis to build the new expression.
   /// Subclasses may override this routine to provide different behavior.
-  ExprResult RebuildCXXNewExpr(SourceLocation StartLoc, bool UseGlobal, bool IsPlacementNewExpr,
+  ExprResult RebuildCXXNewExpr(SourceLocation StartLoc, bool UseGlobal,
+                               bool IsPlacementNewExpr,
                                SourceLocation PlacementLParen,
                                MultiExprArg PlacementArgs,
                                SourceLocation PlacementRParen,
@@ -3351,16 +3352,10 @@ class TreeTransform {
                                TypeSourceInfo *AllocatedTypeInfo,
                                std::optional<Expr *> ArraySize,
                                SourceRange DirectInitRange, Expr *Initializer) {
-    return getSema().BuildCXXNew(StartLoc, UseGlobal, IsPlacementNewExpr,
-                                 PlacementLParen,
-                                 PlacementArgs,
-                                 PlacementRParen,
-                                 TypeIdParens,
-                                 AllocatedType,
-                                 AllocatedTypeInfo,
-                                 ArraySize,
-                                 DirectInitRange,
-                                 Initializer);
+    return getSema().BuildCXXNew(
+        StartLoc, UseGlobal, IsPlacementNewExpr, PlacementLParen, PlacementArgs,
+        PlacementRParen, TypeIdParens, AllocatedType, AllocatedTypeInfo,
+        ArraySize, DirectInitRange, Initializer);
   }
 
   /// Build a new C++ "delete" expression.
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 339b26d44cc8ad0..16cd95dfce36efe 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1047,7 +1047,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
     // where new can return NULL. If we end up supporting that option, we can
     // consider adding a check for it here.
     // C++11 [basic.stc.dynamic.allocation]p3.
-    if (const auto *ProtoType = FD ? FD->getType()->getAs<FunctionProtoType>() : nullptr)
+    if (const auto *ProtoType =
+            FD ? FD->getType()->getAs<FunctionProtoType>() : nullptr)
       if (!ProtoType->isNothrow())
         if (auto dSymVal = symVal.getAs<DefinedOrUnknownSVal>())
           State = State->assume(*dSymVal, true);

>From 4f33aa02dfe2997f174a5c6994f0d70129700868 Mon Sep 17 00:00:00 2001
From: MaxEW707 <82551778+MaxEW707 at users.noreply.github.com>
Date: Tue, 14 Nov 2023 19:24:42 -0500
Subject: [PATCH 3/4] fix formatting errors

---
 clang/lib/AST/ExprConstant.cpp | 4 ++--
 clang/lib/Parse/ParseDecl.cpp  | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 8ee1887c6ccf98a..ecf761feca30f6e 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -3185,8 +3185,8 @@ static bool HandleLValueIndirectMember(EvalInfo &Info, const Expr *E,
 }
 
 /// Get the size of the given type in char units.
-static bool HandleSizeof(EvalInfo &Info, SourceLocation Loc,
-                         QualType Type, CharUnits &Size) {
+static bool HandleSizeof(EvalInfo &Info, SourceLocation Loc, QualType Type,
+                         CharUnits &Size) {
   // sizeof(void), __alignof__(void), sizeof(function) = 1 as a gcc
   // extension.
   if (Type->isVoidType() || Type->isFunctionType()) {
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index c89dfff16c12dde..bca29fc83b540d5 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3295,7 +3295,7 @@ static void SetupFixedPointError(const LangOptions &LangOpts,
                                  bool &isInvalid) {
   assert(!LangOpts.FixedPoint);
   DiagID = diag::err_fixed_point_not_enabled;
-  PrevSpec = "";  // Not used by diagnostic
+  PrevSpec = ""; // Not used by diagnostic
   isInvalid = true;
 }
 

>From a87aa5ff35f3d012b45d8bc65b7b2572f3b6685e Mon Sep 17 00:00:00 2001
From: MaxEW707 <82551778+MaxEW707 at users.noreply.github.com>
Date: Sat, 18 Nov 2023 16:32:40 -0500
Subject: [PATCH 4/4] fix unittest

---
 clang/lib/Sema/SemaExprCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index cf015caffae80ca..0c99b6317f3c5a9 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2304,6 +2304,7 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacementNewExpr,
     UsualArrayDeleteWantsSize =
         doesUsualArrayDeleteWantSize(*this, StartLoc, AllocType);
 
+  SmallVector<Expr *, 8> AllPlaceArgs;
   if (IsPlacementNewExpr && !HaveDependentPlacementTypes) {
     assert(PlacementArgs.size() == 1);
     assert(UseGlobal);
@@ -2324,7 +2325,6 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, bool IsPlacementNewExpr,
     auto *Proto = OperatorNew->getType()->castAs<FunctionProtoType>();
     VariadicCallType CallType = Proto->isVariadic() ? VariadicFunction
                                                     : VariadicDoesNotApply;
-    SmallVector<Expr *, 8> AllPlaceArgs;
 
     // We've already converted the placement args, just fill in any default
     // arguments. Skip the first parameter because we don't have a corresponding



More information about the flang-commits mailing list