Author: Oliver Hunt
Date: 2026-08-08T17:05:53-07:00
New Revision: cd67cfecb1a154d0759783ae926f8b44c104d7d3

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

LOG: [clang] Simplify the overload resolution logic for operator new and new[] 
(#211482)

This PR replaces the current spec-equivalent argument list mutation with
direct iteration of the correctly ordered set of argument lists for a
given allocation.

This adds a bit of architectural work around the argument list
construction but the overall effect is substantially simplified search
of the overload candidates

LLM usage: Claude found align_val_t caching bug, and found and created a
test for reentrant `getTypeIdentityArgument`+`tryBuildStdTypeIdentity`

Added: 
    clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h
    clang/test/SemaCXX/microsoft-new-array-fallback.cpp
    clang/test/SemaCXX/type-aware-new-invalid-alignvalt-cache.cpp

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaCoroutine.cpp
    clang/lib/Sema/SemaExprCXX.cpp
    clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b57ff6c197d1d..51fe39f3733b1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5524,6 +5524,10 @@ def err_no_viable_destructor : Error<
 def err_ambiguous_destructor : Error<
   "destructor of class %0 is ambiguous">;
 
+def note_ovl_ms_allocation_fallback_failed : Note<
+  "Microsoft compatibility array allocation fallback to "
+  "'::operator new(size_t)' failed">;
+
 def err_ovl_no_viable_object_call : Error<
   "no matching function for call to object of type %0">;
 def err_ovl_ambiguous_object_call : Error<

diff  --git a/clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h 
b/clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h
new file mode 100644
index 0000000000000..7ce800111dfe6
--- /dev/null
+++ b/clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h
@@ -0,0 +1,68 @@
+//===- DynamicAllocationArgumentsCXX.h - operator new/delete args 
---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the argument candidate and resolution types for operators
+// new and new[] overload resolution.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SEMA_DYNAMICALLOCATIONARGUMENTSCXX_H
+#define LLVM_CLANG_SEMA_DYNAMICALLOCATIONARGUMENTSCXX_H
+
+#include "clang/AST/ExprCXX.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+
+class LookupResult;
+class Sema;
+
+struct ImplicitAllocationArguments {
+  friend Sema;
+
+  ArrayRef<Expr *> getImplicitArguments() const {
+    return ArrayRef(ImplicitArguments, ArgumentCount);
+  }
+
+  Expr *getAlignmentArgument() const {
+    if (PassAlignment == AlignedAllocationMode::Yes)
+      return ImplicitArguments[ArgumentCount - 1];
+    return nullptr;
+  }
+
+  const LookupResult &
+  updateLookupForMSVCCompatibility(Sema &, const LookupResult &,
+                                   std::optional<LookupResult> &) const;
+  TypeAwareAllocationMode PassTypeIdentity;
+  AlignedAllocationMode PassAlignment;
+  bool IsMSVCCompatibilityFallback;
+
+private:
+  ImplicitAllocationArguments(Sema &SemaRef, Expr *TypeIdentityArg,
+                              Expr *SizeArg, Expr *AlignArg,
+                              bool IsMSVCCompatibilityFallback);
+
+  // Type-identity, size, and alignment
+  static constexpr unsigned MaxImplicitArguments = 3;
+  unsigned ArgumentCount;
+  Expr *ImplicitArguments[MaxImplicitArguments];
+};
+
+struct ResolvedAllocation {
+  FunctionDecl *OperatorNew;
+  FunctionDecl *OperatorDelete;
+  ImplicitAllocationParameters IAP;
+  // type-identity, size, alignment, nothrow or other single placement
+  // parameter
+  SmallVector<Expr *, 4> Arguments;
+};
+
+} // namespace clang
+
+#endif // LLVM_CLANG_SEMA_DYNAMICALLOCATIONARGUMENTSCXX_H

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 778c1a2f5c427..b1d2488d2163b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -136,6 +136,7 @@ struct DeductionFailureInfo;
 class DependentDiagnostic;
 class Designation;
 class IdentifierInfo;
+struct ImplicitAllocationArguments;
 class ImplicitConversionSequence;
 typedef MutableArrayRef<ImplicitConversionSequence> ConversionSequenceList;
 class InitializationKind;
@@ -157,6 +158,7 @@ enum OverloadCandidateRewriteKind : unsigned;
 class OverloadCandidateSet;
 class Preprocessor;
 struct APINotesSelectorDiagnosticState;
+struct ResolvedAllocation;
 class SemaAMDGPU;
 class SemaARM;
 class SemaAVR;
@@ -224,6 +226,10 @@ enum class AssignmentAction {
   Passing_CFAudited
 };
 
+// Inline capacity for type-aware, aligned, and unaligned allocation argument
+// list candidates.
+using AllocationArgumentSet = SmallVector<ImplicitAllocationArguments, 3>;
+
 namespace threadSafety {
 class BeforeSet;
 void threadSafetyCleanup(BeforeSet *Cache);
@@ -8659,12 +8665,11 @@ class Sema final : public SemaBase {
 
   /// Finds the overloads of operator new and delete that are appropriate
   /// for the allocation.
-  bool FindAllocationFunctions(
+  std::optional<ResolvedAllocation> FindAllocationFunctions(
       SourceLocation StartLoc, SourceRange Range,
       AllocationFunctionScope NewScope, AllocationFunctionScope DeleteScope,
-      QualType AllocType, bool IsArray, ImplicitAllocationParameters &IAP,
-      MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew,
-      FunctionDecl *&OperatorDelete, bool Diagnose = true);
+      QualType AllocType, bool IsArray, const ImplicitAllocationParameters 
&IAP,
+      MultiExprArg PlaceArgs, bool Diagnose = true);
 
   /// DeclareGlobalNewDelete - Declare the global forms of operator new and
   /// delete. These are:
@@ -8968,6 +8973,19 @@ class Sema final : public SemaBase {
   void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,
                                  bool DeleteWasArrayForm);
 
+  std::optional<AllocationArgumentSet>
+  resolveAllocationArguments(LookupResult &R,
+                             const ImplicitAllocationParameters &,
+                             ArrayRef<Expr *> PlacementArguments);
+
+  // Attempts to construct the type identity argument for the call to a
+  // type aware operator new. Returns null on failure.
+  Expr *tryGetTypeIdentityArgument(QualType Type, SourceLocation);
+
+  Expr *AllocationSizeExpr = nullptr;
+  Expr *AllocationAlignmentExpr = nullptr;
+  llvm::DenseMap<QualType, Expr *> AllocationTypeIdentityArguments;
+
   ///@}
 
   //

diff  --git a/clang/lib/Sema/SemaCoroutine.cpp 
b/clang/lib/Sema/SemaCoroutine.cpp
index 7f9b1d642cf9d..aceb5f2aa33a4 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -23,6 +23,7 @@
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/DynamicAllocationArgumentsCXX.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Overload.h"
@@ -1478,13 +1479,16 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     IAP = ImplicitAllocationParameters(
         alignedAllocationModeFromBool(ShouldUseAlignedAlloc));
 
-    FunctionDecl *UnusedResult = nullptr;
-    S.FindAllocationFunctions(
+    auto FoundAllocations = S.FindAllocationFunctions(
         Loc, SourceRange(), NewScope,
         /*DeleteScope=*/AllocationFunctionScope::Both, PromiseType,
         /*isArray=*/false, IAP,
-        WithoutPlacementArgs ? MultiExprArg{} : PlacementArgs, OperatorNew,
-        UnusedResult, /*Diagnose=*/false);
+        WithoutPlacementArgs ? MultiExprArg{} : PlacementArgs,
+        /*Diagnose=*/false);
+    if (FoundAllocations) {
+      IAP = FoundAllocations->IAP;
+      OperatorNew = FoundAllocations->OperatorNew;
+    }
     assert(!OperatorNew || !OperatorNew->isTypeAwareOperatorNewOrDelete());
   };
 

diff  --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 538604aa2e64b..6cc80c8254a78 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -32,6 +32,7 @@
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/DynamicAllocationArgumentsCXX.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
@@ -2438,6 +2439,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool 
UseGlobal,
 
   FunctionDecl *OperatorNew = nullptr;
   FunctionDecl *OperatorDelete = nullptr;
+  SmallVector<Expr *, 4> SelectedAllocationArgs;
   unsigned Alignment =
       AllocType->isDependentType() ? 0 : Context.getTypeAlign(AllocType);
   unsigned NewAlignment = Context.getTargetInfo().getNewAlign();
@@ -2454,13 +2456,19 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool 
UseGlobal,
   SourceRange AllocationParameterRange = Range;
   if (PlacementLParen.isValid() && PlacementRParen.isValid())
     AllocationParameterRange = SourceRange(PlacementLParen, PlacementRParen);
-  if (!AllocType->isDependentType() &&
-      !Expr::hasAnyTypeDependentArguments(PlacementArgs) &&
-      FindAllocationFunctions(StartLoc, AllocationParameterRange, Scope, Scope,
-                              AllocType, ArraySize.has_value(), IAP,
-                              PlacementArgs, OperatorNew, OperatorDelete))
-    return ExprError();
 
+  if (!AllocType->isDependentType() &&
+      !Expr::hasAnyTypeDependentArguments(PlacementArgs)) {
+    auto FoundAllocation = FindAllocationFunctions(
+        StartLoc, AllocationParameterRange, Scope, Scope, AllocType,
+        /*IsArray=*/ArraySize.has_value(), IAP, PlacementArgs);
+    if (!FoundAllocation)
+      return ExprError();
+    IAP = FoundAllocation->IAP;
+    OperatorNew = FoundAllocation->OperatorNew;
+    OperatorDelete = FoundAllocation->OperatorDelete;
+    SelectedAllocationArgs = std::move(FoundAllocation->Arguments);
+  }
   // If this is an array allocation, compute whether the usual array
   // deallocation function for the type has a size_t parameter.
   bool UsualArrayDeleteWantsSize = false;
@@ -2479,13 +2487,8 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool 
UseGlobal,
     // arguments. Skip the first parameter because we don't have a 
corresponding
     // argument. Skip the second parameter too if we're passing in the
     // alignment; we've already filled it in.
-    unsigned NumImplicitArgs = 1;
-    if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
-      assert(OperatorNew->isTypeAwareOperatorNewOrDelete());
-      NumImplicitArgs++;
-    }
-    if (isAlignedAllocation(IAP.PassAlignment))
-      NumImplicitArgs++;
+    unsigned NumImplicitArgs =
+        SelectedAllocationArgs.size() - PlacementArgs.size();
     if (GatherArgumentsForCall(AllocationParameterRange.getBegin(), 
OperatorNew,
                                Proto, NumImplicitArgs, PlacementArgs,
                                AllPlaceArgs, CallType))
@@ -2723,9 +2726,9 @@ bool Sema::CheckAllocatedType(QualType AllocType, 
SourceLocation Loc,
 }
 
 static void diagnoseNoViableFunctionForAllocationOverloadResolution(
-    Sema &S, LookupResult &R, SourceRange Range, ArrayRef<Expr *> Args,
+    Sema &S, const LookupResult &R, SourceRange Range, ArrayRef<Expr *> Args,
     OverloadCandidateSet &Candidates, OverloadCandidateSet *AlignedCandidates,
-    Expr *AlignArg) {
+    Expr *AlignArg, bool IncludedMSVCFallback) {
   // If this is an allocation of the form 'new (p) X' for some object
   // pointer p (or an expression that will decay to such a pointer),
   // diagnose the reason for the error.
@@ -2785,114 +2788,81 @@ static void 
diagnoseNoViableFunctionForAllocationOverloadResolution(
     AlignedCandidates->NoteCandidates(S, AlignedArgs, AlignedCands, "",
                                       R.getNameLoc());
   Candidates.NoteCandidates(S, Args, Cands, "", R.getNameLoc());
+  if (IncludedMSVCFallback)
+    S.Diag(R.getNameLoc(), diag::note_ovl_ms_allocation_fallback_failed)
+        << Range;
 }
 
-enum class ResolveMode { Typed, Untyped };
-static bool resolveAllocationOverloadInterior(
-    Sema &S, LookupResult &R, SourceRange Range, ResolveMode Mode,
-    SmallVectorImpl<Expr *> &Args, AlignedAllocationMode &PassAlignment,
-    FunctionDecl *&Operator, OverloadCandidateSet *AlignedCandidates,
-    Expr *AlignArg, bool Diagnose) {
-  unsigned NonTypeArgumentOffset = 0;
-  if (Mode == ResolveMode::Typed) {
-    ++NonTypeArgumentOffset;
-  }
-
-  OverloadCandidateSet Candidates(R.getNameLoc(),
-                                  OverloadCandidateSet::CSK_Normal);
-  for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.end();
+enum class AllocatorResolveResult { Success, Retry, Error };
+static AllocatorResolveResult resolveAllocationOverload(
+    Sema &S, const LookupResult &BaseLookup, SourceRange Range,
+    ImplicitAllocationArguments &AllocationArgs, MultiExprArg TrialArguments,
+    FunctionDecl *&Operator, OverloadCandidateSet &Candidates, bool Diagnose) {
+  std::optional<LookupResult> MSVCFallback;
+  const LookupResult &LocalLookup =
+      AllocationArgs.updateLookupForMSVCCompatibility(S, BaseLookup,
+                                                      MSVCFallback);
+
+  bool ArgumentListIsTypeAware =
+      isTypeAwareAllocation(AllocationArgs.PassTypeIdentity);
+
+  for (LookupResult::iterator Alloc = LocalLookup.begin(),
+                              AllocEnd = LocalLookup.end();
        Alloc != AllocEnd; ++Alloc) {
     // Even member operator new/delete are implicitly treated as
     // static, so don't use AddMemberCandidate.
     NamedDecl *D = (*Alloc)->getUnderlyingDecl();
-    bool IsTypeAware = D->getAsFunction()->isTypeAwareOperatorNewOrDelete();
-    if (IsTypeAware == (Mode != ResolveMode::Typed))
+    bool CandidateIsTypeAware =
+        D->getAsFunction()->isTypeAwareOperatorNewOrDelete();
+    if (CandidateIsTypeAware != ArgumentListIsTypeAware)
       continue;
 
     if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
       S.AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(),
-                                     /*ExplicitTemplateArgs=*/nullptr, Args,
-                                     Candidates,
+                                     /*ExplicitTemplateArgs=*/nullptr,
+                                     TrialArguments, Candidates,
                                      /*SuppressUserConversions=*/false);
       continue;
     }
 
     FunctionDecl *Fn = cast<FunctionDecl>(D);
-    S.AddOverloadCandidate(Fn, Alloc.getPair(), Args, Candidates,
+    S.AddOverloadCandidate(Fn, Alloc.getPair(), TrialArguments, Candidates,
                            /*SuppressUserConversions=*/false);
   }
 
   // Do the resolution.
   OverloadCandidateSet::iterator Best;
-  switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) {
+  switch (Candidates.BestViableFunction(S, LocalLookup.getNameLoc(), Best)) {
   case OR_Success: {
-    // Got one!
     FunctionDecl *FnDecl = Best->Function;
-    if (S.CheckAllocationAccess(R.getNameLoc(), Range, R.getNamingClass(),
+    if (S.CheckAllocationAccess(LocalLookup.getNameLoc(), Range,
+                                LocalLookup.getNamingClass(),
                                 Best->FoundDecl) == Sema::AR_inaccessible)
-      return true;
+      return AllocatorResolveResult::Error;
 
     Operator = FnDecl;
-    return false;
+    return AllocatorResolveResult::Success;
   }
 
   case OR_No_Viable_Function:
-    // C++17 [expr.new]p13:
-    //   If no matching function is found and the allocated object type has
-    //   new-extended alignment, the alignment argument is removed from the
-    //   argument list, and overload resolution is performed again.
-    if (isAlignedAllocation(PassAlignment)) {
-      PassAlignment = AlignedAllocationMode::No;
-      AlignArg = Args[NonTypeArgumentOffset + 1];
-      Args.erase(Args.begin() + NonTypeArgumentOffset + 1);
-      return resolveAllocationOverloadInterior(S, R, Range, Mode, Args,
-                                               PassAlignment, Operator,
-                                               &Candidates, AlignArg, 
Diagnose);
-    }
-
-    // MSVC will fall back on trying to find a matching global operator new
-    // if operator new[] cannot be found.  Also, MSVC will leak by not
-    // generating a call to operator delete or operator delete[], but we
-    // will not replicate that bug.
-    // FIXME: Find out how this interacts with the std::align_val_t fallback
-    // once MSVC implements it.
-    if (R.getLookupName().getCXXOverloadedOperator() == OO_Array_New &&
-        S.Context.getLangOpts().MSVCCompat && Mode != ResolveMode::Typed) {
-      R.clear();
-      R.setLookupName(S.Context.DeclarationNames.getCXXOperatorName(OO_New));
-      S.LookupQualifiedName(R, S.Context.getTranslationUnitDecl());
-      // FIXME: This will give bad diagnostics pointing at the wrong functions.
-      return resolveAllocationOverloadInterior(S, R, Range, Mode, Args,
-                                               PassAlignment, Operator,
-                                               /*Candidates=*/nullptr,
-                                               /*AlignArg=*/nullptr, Diagnose);
-    }
-    if (Mode == ResolveMode::Typed) {
-      // If we can't find a matching type aware operator we don't consider this
-      // a failure.
-      Operator = nullptr;
-      return false;
-    }
-    if (Diagnose)
-      diagnoseNoViableFunctionForAllocationOverloadResolution(
-          S, R, Range, Args, Candidates, AlignedCandidates, AlignArg);
-    return true;
+    return AllocatorResolveResult::Retry;
 
   case OR_Ambiguous:
     if (Diagnose) {
       Candidates.NoteCandidates(
-          PartialDiagnosticAt(R.getNameLoc(),
+          PartialDiagnosticAt(LocalLookup.getNameLoc(),
                               S.PDiag(diag::err_ovl_ambiguous_call)
-                                  << R.getLookupName() << Range),
-          S, OCD_AmbiguousCandidates, Args);
+                                  << LocalLookup.getLookupName() << Range),
+          S, OCD_AmbiguousCandidates, TrialArguments);
     }
-    return true;
+    return AllocatorResolveResult::Error;
 
   case OR_Deleted: {
     if (Diagnose)
-      S.DiagnoseUseOfDeletedFunction(R.getNameLoc(), Range, R.getLookupName(),
-                                     Candidates, Best->Function, Args);
-    return true;
+      S.DiagnoseUseOfDeletedFunction(LocalLookup.getNameLoc(), Range,
+                                     LocalLookup.getLookupName(), Candidates,
+                                     Best->Function, TrialArguments);
+    return AllocatorResolveResult::Error;
   }
   }
   llvm_unreachable("Unreachable, bad result from BestViableFunction");
@@ -2918,55 +2888,187 @@ static void LookupGlobalDeallocationFunctions(Sema &S, 
SourceLocation Loc,
   }
 }
 
-static bool resolveAllocationOverload(
-    Sema &S, LookupResult &R, SourceRange Range, SmallVectorImpl<Expr *> &Args,
-    ImplicitAllocationParameters &IAP, FunctionDecl *&Operator,
-    OverloadCandidateSet *AlignedCandidates, Expr *AlignArg, bool Diagnose) {
-  Operator = nullptr;
+static void
+DiagnoseAllocationLookupFailure(Sema &SemaRef, const LookupResult &R,
+                                SourceRange Range,
+                                AllocationArgumentSet &ArgumentCandidates,
+                                ArrayRef<Expr *> PlacementArguments) {
+  ImplicitAllocationArguments *UnalignedArgumentList = nullptr;
+  ImplicitAllocationArguments *AlignedArgumentList = nullptr;
+  bool IncludedMSVCFallback = false;
+  for (ImplicitAllocationArguments &AllocationArguments : ArgumentCandidates) {
+    if (AllocationArguments.IsMSVCCompatibilityFallback) {
+      IncludedMSVCFallback = true;
+      continue;
+    }
+    if (AllocationArguments.PassTypeIdentity == TypeAwareAllocationMode::Yes)
+      continue;
+    if (AllocationArguments.PassAlignment == AlignedAllocationMode::Yes)
+      AlignedArgumentList = &AllocationArguments;
+    else
+      UnalignedArgumentList = &AllocationArguments;
+  }
+  if (!UnalignedArgumentList)
+    return;
+
+  // We re-resolve the rejected candidates for diagnostics rather than 
requiring
+  // them to be tracked during the initial resolution path. This both 
simplifies
+  // the resolution logic, and helps with performance.
+  auto Rerun = [&](ImplicitAllocationArguments &ArgumentList,
+                   OverloadCandidateSet &Candidates,
+                   SmallVectorImpl<Expr *> &Args) {
+    assert(!ArgumentList.IsMSVCCompatibilityFallback);
+    llvm::append_range(Args, ArgumentList.getImplicitArguments());
+    llvm::append_range(Args, PlacementArguments);
+    FunctionDecl *Unused = nullptr;
+    resolveAllocationOverload(SemaRef, R, Range, ArgumentList, Args, Unused,
+                              Candidates, /*Diagnose=*/false);
+  };
+  std::optional<OverloadCandidateSet> AlignedCandidates;
+  Expr *AlignArg = nullptr;
+  if (AlignedArgumentList) {
+    AlignedCandidates.emplace(R.getNameLoc(), 
OverloadCandidateSet::CSK_Normal);
+    SmallVector<Expr *, 4> AlignedArgs;
+    Rerun(*AlignedArgumentList, *AlignedCandidates, AlignedArgs);
+    AlignArg = AlignedArgumentList->getAlignmentArgument();
+  }
+  OverloadCandidateSet UnalignedCandidates(R.getNameLoc(),
+                                           OverloadCandidateSet::CSK_Normal);
+  SmallVector<Expr *, 4> UnalignedArgs;
+  Rerun(*UnalignedArgumentList, UnalignedCandidates, UnalignedArgs);
+  diagnoseNoViableFunctionForAllocationOverloadResolution(
+      SemaRef, R, Range, UnalignedArgs, UnalignedCandidates,
+      AlignedCandidates ? &*AlignedCandidates : nullptr, AlignArg,
+      IncludedMSVCFallback);
+}
+
+Expr *Sema::tryGetTypeIdentityArgument(QualType Type, SourceLocation Loc) {
+  if (auto Found = AllocationTypeIdentityArguments.find(Type);
+      Found != AllocationTypeIdentityArguments.end())
+    return Found->second;
+
+  QualType TypeIdentity = tryBuildStdTypeIdentity(Type, Loc);
+  if (TypeIdentity.isNull() ||
+      RequireCompleteType(Loc, TypeIdentity, diag::err_incomplete_type))
+    return nullptr;
+
+  Expr *TypeIdentityArgument =
+      new (Context) CXXScalarValueInitExpr(TypeIdentity, nullptr, Loc);
+  AllocationTypeIdentityArguments.insert({Type, TypeIdentityArgument});
+  return TypeIdentityArgument;
+}
+
+ImplicitAllocationArguments::ImplicitAllocationArguments(
+    Sema &SemaRef, Expr *TypeIdentityArg, Expr *SizeArg, Expr *AlignArg,
+    bool IsMSVCCompatibilityFallback)
+    : PassTypeIdentity(typeAwareAllocationModeFromBool(TypeIdentityArg)),
+      PassAlignment(alignedAllocationModeFromBool(AlignArg)),
+      IsMSVCCompatibilityFallback(IsMSVCCompatibilityFallback),
+      ArgumentCount(0) {
+  ASTContext &Ctx = SemaRef.getASTContext();
+  if (TypeIdentityArg) {
+    assert(SemaRef.isStdTypeIdentity(TypeIdentityArg->getType(), nullptr));
+    ImplicitArguments[ArgumentCount++] = TypeIdentityArg;
+  }
+  assert(SizeArg);
+  assert(Ctx.hasSameType(SizeArg->getType(), Ctx.getSizeType()));
+  ImplicitArguments[ArgumentCount++] = SizeArg;
+  if (AlignArg) {
+    assert(AlignArg->getType()->isAlignValT());
+    ImplicitArguments[ArgumentCount++] = AlignArg;
+  }
+}
+
+const LookupResult &
+ImplicitAllocationArguments::updateLookupForMSVCCompatibility(
+    Sema &S, const LookupResult &BaseLookup,
+    std::optional<LookupResult> &Buffer) const {
+  if (!IsMSVCCompatibilityFallback)
+    return BaseLookup;
+  // MSVC will fall back on trying to find a matching global operator new
+  // if operator new[] cannot be found.  Also, MSVC will leak by not
+  // generating a call to operator delete or operator delete[], but we
+  // will not replicate that bug.
+  // FIXME: Find out how this interacts with the std::align_val_t fallback
+  // once MSVC implements it.
+  LookupResult &Fallback = Buffer.emplace(LookupResult::Temporary, BaseLookup);
+  
Fallback.setLookupName(S.Context.DeclarationNames.getCXXOperatorName(OO_New));
+  // FIXME: This will give bad diagnostics pointing at the wrong functions.
+  S.LookupQualifiedName(Fallback, S.Context.getTranslationUnitDecl());
+  return Fallback;
+}
+
+std::optional<AllocationArgumentSet>
+Sema::resolveAllocationArguments(LookupResult &R,
+                                 const ImplicitAllocationParameters &IAP,
+                                 ArrayRef<Expr *> PlacementArguments) {
+  // FIXME: Should Sema create per-callsite versions expressions so they can be
+  // reused during codegen? This would likely create yet another case where we
+  // need to serialize information, however it would ensure identical arguments
+  // between Sema and CodeGen.
+  if (!AllocationSizeExpr) {
+    DeclareGlobalNewDelete();
+    QualType SizeTy = Context.getSizeType();
+    unsigned SizeTyWidth = Context.getTypeSize(SizeTy);
+    AllocationSizeExpr = IntegerLiteral::Create(
+        Context, llvm::APInt::getZero(SizeTyWidth), SizeTy, SourceLocation());
+  }
+  if (!AllocationAlignmentExpr) {
+    DeclareGlobalNewDelete();
+    if (EnumDecl *StdAlignValT = getStdAlignValT()) {
+      QualType AlignValT = Context.getCanonicalTagType(StdAlignValT);
+      AllocationAlignmentExpr = new (Context)
+          CXXScalarValueInitExpr(AlignValT, nullptr, SourceLocation());
+    }
+  }
+
+  AllocationArgumentSet FoundArguments;
   if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
-    assert(S.isStdTypeIdentity(Args[0]->getType(), nullptr));
-    // The internal overload resolution work mutates the argument list
-    // in accordance with the spec. We may want to change that in future,
-    // but for now we deal with this by making a copy of the non-type-identity
-    // arguments.
-    SmallVector<Expr *> UntypedParameters;
-    UntypedParameters.reserve(Args.size() - 1);
-    UntypedParameters.push_back(Args[1]);
-    // Type aware allocation implicitly includes the alignment parameter so
-    // only include it in the untyped parameter list if alignment was 
explicitly
-    // requested
-    if (isAlignedAllocation(IAP.PassAlignment))
-      UntypedParameters.push_back(Args[2]);
-    UntypedParameters.append(Args.begin() + 3, Args.end());
-
-    AlignedAllocationMode InitialAlignmentMode = IAP.PassAlignment;
-    IAP.PassAlignment = AlignedAllocationMode::Yes;
-    if (resolveAllocationOverloadInterior(
-            S, R, Range, ResolveMode::Typed, Args, IAP.PassAlignment, Operator,
-            AlignedCandidates, AlignArg, Diagnose))
-      return true;
-    if (Operator)
-      return false;
+    Expr *TypeIdentityArgument =
+        tryGetTypeIdentityArgument(IAP.Type, R.getNameLoc());
+    if (!TypeIdentityArgument)
+      return std::nullopt;
+
+    Expr *AlignmentExpr = AllocationAlignmentExpr;
+    if (!PlacementArguments.empty() &&
+        PlacementArguments.front()->getType()->isAlignValT())
+      AlignmentExpr = nullptr;
+    FoundArguments.push_back(ImplicitAllocationArguments(
+        *this, TypeIdentityArgument, AllocationSizeExpr, AlignmentExpr,
+        /*IsMSVCCompatibilityFallback=*/false));
+  }
+
+  ImplicitAllocationArguments UnalignedArguments(
+      *this, /*TypeIdentityArg=*/nullptr, AllocationSizeExpr,
+      /*AlignArg=*/nullptr, /*IsMSVCCompatibilityFallback=*/false);
+  ImplicitAllocationArguments AlignedArguments(
+      *this, /*TypeIdentityArg=*/nullptr, AllocationSizeExpr,
+      AllocationAlignmentExpr, /*IsMSVCCompatibilityFallback=*/false);
+
+  // C++17 [expr.new]p13:
+  //   If no matching function is found and the allocated object type has
+  //   new-extended alignment, the alignment argument is removed from the
+  //   argument list, and overload resolution is performed again.
+  if (IAP.PassAlignment == AlignedAllocationMode::Yes)
+    FoundArguments.push_back(AlignedArguments);
+  FoundArguments.push_back(UnalignedArguments);
+
+  // The MSVC global fallback path
+  if (getLangOpts().MSVCCompat &&
+      R.getLookupName().getCXXOverloadedOperator() == OO_Array_New)
+    FoundArguments.push_back(ImplicitAllocationArguments(
+        *this, /*TypeIdentityArg=*/nullptr, AllocationSizeExpr,
+        /*AlignArg=*/nullptr, /*IsMSVCCompatibilityFallback=*/true));
+  return FoundArguments;
+}
 
-    // If we got to this point we could not find a matching typed operator
-    // so we update the IAP flags, and revert to our stored copy of the
-    // type-identity-less argument list.
-    IAP.PassTypeIdentity = TypeAwareAllocationMode::No;
-    IAP.PassAlignment = InitialAlignmentMode;
-    Args = std::move(UntypedParameters);
-  }
-  assert(!S.isStdTypeIdentity(Args[0]->getType(), nullptr));
-  return resolveAllocationOverloadInterior(
-      S, R, Range, ResolveMode::Untyped, Args, IAP.PassAlignment, Operator,
-      AlignedCandidates, AlignArg, Diagnose);
-}
-
-bool Sema::FindAllocationFunctions(
-    SourceLocation StartLoc, SourceRange Range,
-    AllocationFunctionScope NewScope, AllocationFunctionScope DeleteScope,
-    QualType AllocType, bool IsArray, ImplicitAllocationParameters &IAP,
-    MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew,
-    FunctionDecl *&OperatorDelete, bool Diagnose) {
+std::optional<ResolvedAllocation>
+Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
+                              AllocationFunctionScope NewScope,
+                              AllocationFunctionScope DeleteScope,
+                              QualType AllocType, bool IsArray,
+                              const ImplicitAllocationParameters &RequestedIAP,
+                              MultiExprArg PlaceArgs, bool Diagnose) {
   // --- Choosing an allocation function ---
   // C++ 5.3.4p8 - 14 & 18
   // 1) If looking in AllocationFunctionScope::Global scope for allocation
@@ -2979,9 +3081,6 @@ bool Sema::FindAllocationFunctions(
   // 3) The first argument is always size_t. Append the arguments from the
   //   placement form.
 
-  SmallVector<Expr*, 8> AllocArgs;
-  AllocArgs.reserve(IAP.getNumImplicitArgs() + PlaceArgs.size());
-
   // C++ [expr.new]p8:
   //   If the allocated type is a non-array type, the allocation
   //   function's name is operator new and the deallocation function's
@@ -2993,49 +3092,10 @@ bool Sema::FindAllocationFunctions(
 
   QualType AllocElemType = Context.getBaseElementType(AllocType);
 
-  // We don't care about the actual value of these arguments.
-  // FIXME: Should the Sema create the expression and embed it in the syntax
-  // tree? Or should the consumer just recalculate the value?
-  // FIXME: Using a dummy value will interact poorly with attribute enable_if.
-
-  // We use size_t as a stand in so that we can construct the init
-  // expr on the stack
-  QualType TypeIdentity = Context.getSizeType();
-  if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
-    QualType SpecializedTypeIdentity =
-        tryBuildStdTypeIdentity(IAP.Type, StartLoc);
-    if (!SpecializedTypeIdentity.isNull()) {
-      TypeIdentity = SpecializedTypeIdentity;
-      if (RequireCompleteType(StartLoc, TypeIdentity,
-                              diag::err_incomplete_type))
-        return true;
-    } else
-      IAP.PassTypeIdentity = TypeAwareAllocationMode::No;
-  }
-  TypeAwareAllocationMode OriginalTypeAwareState = IAP.PassTypeIdentity;
-
-  CXXScalarValueInitExpr TypeIdentityParam(TypeIdentity, nullptr, StartLoc);
-  if (isTypeAwareAllocation(IAP.PassTypeIdentity))
-    AllocArgs.push_back(&TypeIdentityParam);
-
-  QualType SizeTy = Context.getSizeType();
-  unsigned SizeTyWidth = Context.getTypeSize(SizeTy);
-  IntegerLiteral Size(Context, llvm::APInt::getZero(SizeTyWidth), SizeTy,
-                      SourceLocation());
-  AllocArgs.push_back(&Size);
-
-  QualType AlignValT = Context.VoidTy;
-  bool IncludeAlignParam = isAlignedAllocation(IAP.PassAlignment) ||
-                           isTypeAwareAllocation(IAP.PassTypeIdentity);
-  if (IncludeAlignParam) {
-    DeclareGlobalNewDelete();
-    AlignValT = Context.getCanonicalTagType(getStdAlignValT());
-  }
-  CXXScalarValueInitExpr Align(AlignValT, nullptr, SourceLocation());
-  if (IncludeAlignParam)
-    AllocArgs.push_back(&Align);
-
-  llvm::append_range(AllocArgs, PlaceArgs);
+  ResolvedAllocation Result = {/*OperatorNew=*/nullptr,
+                               /*OperatorDelete=*/nullptr,
+                               RequestedIAP,
+                               {}};
 
   // Find the allocation function.
   {
@@ -3053,14 +3113,14 @@ bool Sema::FindAllocationFunctions(
     // We can see ambiguity here if the allocation function is found in
     // multiple base classes.
     if (R.isAmbiguous())
-      return true;
+      return std::nullopt;
 
     //   If this lookup fails to find the name, or if the allocated type is not
     //   a class type, the allocation function's name is looked up in the
     //   global scope.
     if (R.empty()) {
       if (NewScope == AllocationFunctionScope::Class)
-        return true;
+        return std::nullopt;
 
       LookupQualifiedName(R, Context.getTranslationUnitDecl());
     }
@@ -3071,7 +3131,7 @@ bool Sema::FindAllocationFunctions(
       } else {
         Diag(StartLoc, diag::err_openclcxx_placement_new);
       }
-      return true;
+      return std::nullopt;
     }
 
     assert(!R.empty() && "implicitly declared allocation functions not found");
@@ -3080,17 +3140,44 @@ bool Sema::FindAllocationFunctions(
     // We do our own custom access checks below.
     R.suppressDiagnostics();
 
-    if (resolveAllocationOverload(*this, R, Range, AllocArgs, IAP, OperatorNew,
-                                  /*Candidates=*/nullptr,
-                                  /*AlignArg=*/nullptr, Diagnose))
-      return true;
+    std::optional<AllocationArgumentSet> ArgumentListCandidates =
+        resolveAllocationArguments(R, RequestedIAP, PlaceArgs);
+    if (!ArgumentListCandidates)
+      return std::nullopt;
+
+    for (ImplicitAllocationArguments &ArgumentList : *ArgumentListCandidates) {
+      SmallVector<Expr *, 4> TrialArguments(
+          ArgumentList.getImplicitArguments());
+      llvm::append_range(TrialArguments, PlaceArgs);
+      OverloadCandidateSet OverloadCandidates(R.getNameLoc(),
+                                              
OverloadCandidateSet::CSK_Normal);
+      FunctionDecl *Operator = nullptr;
+      switch (resolveAllocationOverload(*this, R, Range, ArgumentList,
+                                        TrialArguments, Operator,
+                                        OverloadCandidates, Diagnose)) {
+      case AllocatorResolveResult::Error:
+        return std::nullopt;
+      case AllocatorResolveResult::Retry:
+        continue;
+      case AllocatorResolveResult::Success:
+        Result.OperatorNew = Operator;
+        Result.IAP.PassTypeIdentity = ArgumentList.PassTypeIdentity;
+        Result.IAP.PassAlignment = ArgumentList.PassAlignment;
+        Result.Arguments = std::move(TrialArguments);
+        goto foundCandidate;
+      }
+    }
+    if (Diagnose)
+      DiagnoseAllocationLookupFailure(*this, R, Range, *ArgumentListCandidates,
+                                      PlaceArgs);
+    return std::nullopt;
   }
+foundCandidate:
+  FunctionDecl *OperatorNew = Result.OperatorNew;
 
   // We don't need an operator delete if we're running under -fno-exceptions.
-  if (!getLangOpts().Exceptions) {
-    OperatorDelete = nullptr;
-    return false;
-  }
+  if (!getLangOpts().Exceptions)
+    return Result;
 
   // Note, the name of OperatorNew might have been changed from array to
   // non-array by resolveAllocationOverload.
@@ -3115,7 +3202,7 @@ bool Sema::FindAllocationFunctions(
     LookupQualifiedName(FoundDelete, RD);
   }
   if (FoundDelete.isAmbiguous())
-    return true; // FIXME: clean up expressions?
+    return std::nullopt; // FIXME: clean up expressions?
 
   // Filter out any destroying operator deletes. We can't possibly call such a
   // function in this context, because we're handling the case where the object
@@ -3139,10 +3226,10 @@ bool Sema::FindAllocationFunctions(
 
   bool FoundGlobalDelete = FoundDelete.empty();
   bool IsClassScopedTypeAwareNew =
-      isTypeAwareAllocation(IAP.PassTypeIdentity) &&
+      isTypeAwareAllocation(Result.IAP.PassTypeIdentity) &&
       OperatorNewContext->isRecord();
   auto DiagnoseMissingTypeAwareCleanupOperator = [&](bool IsPlacementOperator) 
{
-    assert(isTypeAwareAllocation(IAP.PassTypeIdentity));
+    assert(isTypeAwareAllocation(Result.IAP.PassTypeIdentity));
     if (Diagnose) {
       Diag(StartLoc, diag::err_mismatching_type_aware_cleanup_deallocator)
           << OperatorNew->getDeclName() << IsPlacementOperator << DeleteName;
@@ -3153,18 +3240,19 @@ bool Sema::FindAllocationFunctions(
   };
   if (IsClassScopedTypeAwareNew && FoundDelete.empty()) {
     DiagnoseMissingTypeAwareCleanupOperator(/*isPlacementNew=*/false);
-    return true;
+    return std::nullopt;
   }
   if (FoundDelete.empty()) {
     FoundDelete.clear(LookupOrdinaryName);
 
     if (DeleteScope == AllocationFunctionScope::Class)
-      return true;
+      return std::nullopt;
 
     DeclareGlobalNewDelete();
-    DeallocLookupMode LookupMode = 
isTypeAwareAllocation(OriginalTypeAwareState)
-                                       ? DeallocLookupMode::OptionallyTyped
-                                       : DeallocLookupMode::Untyped;
+    DeallocLookupMode LookupMode =
+        isTypeAwareAllocation(RequestedIAP.PassTypeIdentity)
+            ? DeallocLookupMode::OptionallyTyped
+            : DeallocLookupMode::Untyped;
     LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
                                       DeleteName);
   }
@@ -3188,7 +3276,7 @@ bool Sema::FindAllocationFunctions(
   // type uses the sized or non-sized form of aligned operator delete.
 
   unsigned NonPlacementNewArgCount = 1; // size parameter
-  if (isTypeAwareAllocation(IAP.PassTypeIdentity))
+  if (isTypeAwareAllocation(Result.IAP.PassTypeIdentity))
     NonPlacementNewArgCount =
         /* type-identity */ 1 + /* size */ 1 + /* alignment */ 1;
   bool isPlacementNew = !PlaceArgs.empty() ||
@@ -3212,8 +3300,8 @@ bool Sema::FindAllocationFunctions(
 
       SmallVector<QualType, 6> ArgTypes;
       int InitialParamOffset = 0;
-      if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
-        ArgTypes.push_back(TypeIdentity);
+      if (isTypeAwareAllocation(Result.IAP.PassTypeIdentity)) {
+        ArgTypes.push_back(Result.Arguments.front()->getType());
         InitialParamOffset = 1;
       }
       ArgTypes.push_back(Context.VoidPtrTy);
@@ -3255,9 +3343,9 @@ bool Sema::FindAllocationFunctions(
     if (getLangOpts().CUDA)
       CUDA().EraseUnwantedMatches(getCurFunctionDecl(/*AllowLambda=*/true),
                                   Matches);
-    if (Matches.empty() && isTypeAwareAllocation(IAP.PassTypeIdentity)) {
+    if (Matches.empty() && isTypeAwareAllocation(Result.IAP.PassTypeIdentity)) 
{
       DiagnoseMissingTypeAwareCleanupOperator(isPlacementNew);
-      return true;
+      return std::nullopt;
     }
   } else {
     // C++1y [expr.new]p22:
@@ -3269,7 +3357,7 @@ bool Sema::FindAllocationFunctions(
     // with a size_t where possible (which it always is in this case).
     llvm::SmallVector<UsualDeallocFnInfo, 4> BestDeallocFns;
     ImplicitDeallocationParameters IDP = {
-        AllocElemType, OriginalTypeAwareState,
+        AllocElemType, RequestedIAP.PassTypeIdentity,
         alignedAllocationModeFromBool(
             hasNewExtendedAlignment(*this, AllocElemType)),
         sizedDeallocationModeFromBool(FoundGlobalDelete)};
@@ -3290,7 +3378,8 @@ bool Sema::FindAllocationFunctions(
   //   function, that function will be called; otherwise, no
   //   deallocation function will be called.
   if (Matches.size() == 1) {
-    OperatorDelete = Matches[0].second;
+    Result.OperatorDelete = Matches[0].second;
+    FunctionDecl *OperatorDelete = Result.OperatorDelete;
     DeclContext *OperatorDeleteContext = GetRedeclContext(OperatorDelete);
     bool FoundTypeAwareOperator =
         OperatorDelete->isTypeAwareOperatorNewOrDelete() ||
@@ -3348,7 +3437,8 @@ bool Sema::FindAllocationFunctions(
           IsSizedDelete = false;
       }
 
-      if (IsSizedDelete && !isTypeAwareAllocation(IAP.PassTypeIdentity)) {
+      if (IsSizedDelete &&
+          !isTypeAwareAllocation(Result.IAP.PassTypeIdentity)) {
         SourceRange R = PlaceArgs.empty()
                             ? SourceRange()
                             : SourceRange(PlaceArgs.front()->getBeginLoc(),
@@ -3362,7 +3452,7 @@ bool Sema::FindAllocationFunctions(
     if (CheckDeleteOperator(*this, StartLoc, Range, Diagnose,
                             FoundDelete.getNamingClass(), Matches[0].first,
                             Matches[0].second))
-      return true;
+      return std::nullopt;
 
   } else if (!Matches.empty()) {
     // We found multiple suitable operators. Per [expr.new]p20, that means we
@@ -3376,7 +3466,7 @@ bool Sema::FindAllocationFunctions(
            diag::note_member_declared_here) << DeleteName;
   }
 
-  return false;
+  return Result;
 }
 
 void Sema::DeclareGlobalNewDelete() {

diff  --git a/clang/test/SemaCXX/microsoft-new-array-fallback.cpp 
b/clang/test/SemaCXX/microsoft-new-array-fallback.cpp
new file mode 100644
index 0000000000000..7e2ca1bc5a4c9
--- /dev/null
+++ b/clang/test/SemaCXX/microsoft-new-array-fallback.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fms-compatibility -fsyntax-only -verify -std=c++11 %s
+
+typedef __SIZE_TYPE__ size_t;
+
+void *operator new[](size_t); // #new_array_decl
+void *operator new(size_t); // #new_decl
+
+struct Tag {};
+
+void f() {
+  int *p = new (Tag{}) int[4]; // #new_expr
+  // expected-error@#new_expr {{no matching function for call to 'operator 
new[]'}}
+  // expected-note@#new_array_decl {{candidate function not viable: requires 1 
argument, but 2 were provided}}
+  // expected-note@#new_expr {{Microsoft compatibility array allocation 
fallback to '::operator new(size_t)' failed}}
+  (void)p;
+}

diff  --git a/clang/test/SemaCXX/type-aware-new-invalid-alignvalt-cache.cpp 
b/clang/test/SemaCXX/type-aware-new-invalid-alignvalt-cache.cpp
new file mode 100644
index 0000000000000..b1b9e67d6203a
--- /dev/null
+++ b/clang/test/SemaCXX/type-aware-new-invalid-alignvalt-cache.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -std=c++26 \
+// RUN: -fno-aligned-allocation -Wno-ext-cxx-type-aware-allocators -verify %s
+
+void first() {
+  new int;
+}
+
+namespace std {
+  using size_t = __SIZE_TYPE__;
+  template <class T> struct type_identity { using type = T; };
+}
+
+void second() {
+  new float;
+}
+
+namespace std {
+  enum class align_val_t : size_t {};
+}
+
+template <class T> void *operator new(std::type_identity<T>, std::size_t, 
std::align_val_t) = delete; // #new_decl
+template <class T> void operator delete(std::type_identity<T>, void *, 
std::size_t, std::align_val_t) = delete;
+
+struct Foo {
+  int x; 
+};
+
+void third() {
+  (void)new Foo; // #new_expr
+  // expected-error@#new_expr {{call to deleted function 'operator new'}}
+  // expected-note@#new_decl {{candidate function [with T = Foo] has been 
explicitly deleted}}
+}

diff  --git a/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp 
b/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
index 502f4fab6b519..7456e2db7eeda 100644
--- a/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
+++ b/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
@@ -3,11 +3,11 @@
 // RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s 
-Wno-ext-cxx-type-aware-allocators -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=2
 // RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s 
-Wno-ext-cxx-type-aware-allocators -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=3
 // RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s 
-Wno-ext-cxx-type-aware-allocators -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=4
+// RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s 
-Wno-ext-cxx-type-aware-allocators -std=c++26 -DINVALID_TYPE_IDENTITY_VERSION=5
 // RUN: %clang_cc1 -triple arm64-apple-macosx -fsyntax-only -verify %s 
-Wno-ext-cxx-type-aware-allocators -std=c++26
 
 namespace std {
 #if !defined(INVALID_TYPE_IDENTITY_VERSION)
-  // expected-no-diagnostics
   template <class T> struct type_identity {
   };
   #define TYPE_IDENTITY(T) std::type_identity<T>
@@ -30,6 +30,11 @@ namespace std {
   template <class T> struct inner {};
   template <class T> using type_identity = inner<T>;
   #define TYPE_IDENTITY(T) std::type_identity<T>
+#elif INVALID_TYPE_IDENTITY_VERSION==5 
+template <class T> struct type_identity { // #reentrant_type_identity_decl
+  using type = decltype(new T); // #reentrant_type_identity_type_decl
+};
+#define TYPE_IDENTITY(T) std::type_identity<T>
 #endif
   using size_t = __SIZE_TYPE__;
   enum class align_val_t : long {};
@@ -38,6 +43,14 @@ namespace std {
 template <class T> void *operator new(TYPE_IDENTITY(T), std::size_t, 
std::align_val_t); // #operator_new
 template <class T> void operator delete(TYPE_IDENTITY(T), void*, std::size_t, 
std::align_val_t); // #operator_delete
 
+using size_t = __SIZE_TYPE__;
+struct TestType {};
+
+void reentrant_type_identity() {
+  TestType *t = new TestType; // #reentrant_new
+  delete t;
+}
+
 // These error messages aren't great, but they fall out of the way we model
 // alias types. Getting them in this way requires extremely unlikely code to be
 // used, so this is not terrible.
@@ -48,12 +61,24 @@ template <class T> void operator delete(TYPE_IDENTITY(T), 
void*, std::size_t, st
 #elif INVALID_TYPE_IDENTITY_VERSION==4
 // expected-error@#operator_new {{'operator new' cannot take a dependent type 
as its 1st parameter; use size_t ('unsigned long') instead}}
 // expected-error@#operator_delete {{'operator delete' cannot take a dependent 
type as its 1st parameter; use 'void *' instead}}
+#elif INVALID_TYPE_IDENTITY_VERSION==5
+// expected-error@#reentrant_type_identity_type_decl {{incomplete type 
'std::type_identity<TestType>' where a complete type is required}}
+// expected-note@#reentrant_type_identity_decl {{definition of 
'std::type_identity<TestType>' is not complete until the closing '}'}}
+// expected-note@#reentrant_new {{in instantiation of template class 
'std::type_identity<TestType>' requested here}}
 #endif
 
-using size_t = __SIZE_TYPE__;
-struct TestType {};
+#if !defined(INVALID_TYPE_IDENTITY_VERSION)
+struct Bad {};
+template <> struct std::type_identity<Bad>; // #incomplete_specialization
 
-void f() {
-  TestType *t = new TestType;
-  delete t;
+// This is a pure implementation test to ensure correct caching behavior if
+// constructing the type_identity argument fails.
+void failedTypeIdentitySpecialization() {
+  Bad *a = new Bad;
+  // expected-error@-1 {{incomplete type 'std::type_identity<Bad>' where a 
complete type is required}}
+  // expected-note@#incomplete_specialization {{forward declaration of 
'std::type_identity<Bad>'}}
+  Bad *b = new Bad;
+  // expected-error@-1 {{incomplete type 'std::type_identity<Bad>' where a 
complete type is required}}
+  // expected-note@#incomplete_specialization {{forward declaration of 
'std::type_identity<Bad>'}}
 }
+#endif


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to