https://github.com/ojhunt updated 
https://github.com/llvm/llvm-project/pull/211482

>From 9213eed76e07c0f7931675bcf4e8f4865a422e2d Mon Sep 17 00:00:00 2001
From: Oliver Hunt <[email protected]>
Date: Thu, 23 Jul 2026 00:20:07 -0700
Subject: [PATCH 1/4] [clang] Simplify the overload resolution logic for
 operator new and new[]

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
---
 clang/include/clang/Sema/Sema.h               |  67 ++-
 clang/lib/Sema/SemaCoroutine.cpp              |  11 +-
 clang/lib/Sema/SemaExprCXX.cpp                | 477 ++++++++++--------
 clang/lib/Sema/SemaOverload.cpp               |   2 +-
 .../SemaCXX/microsoft-new-array-fallback.cpp  |  14 +
 .../type-aware-new-invalid-type-identity.cpp  |  15 +-
 6 files changed, 378 insertions(+), 208 deletions(-)
 create mode 100644 clang/test/SemaCXX/microsoft-new-array-fallback.cpp

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d43b6954196a2..292c53f460826 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8651,14 +8651,52 @@ class Sema final : public SemaBase {
   bool CheckAllocatedType(QualType AllocType, SourceLocation Loc,
                           SourceRange R);
 
+  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;
+    }
+
+    void updateLookupForMSVCCompatibility(Sema &, LookupResult &);
+    TypeAwareAllocationMode PassTypeIdentity;
+    AlignedAllocationMode PassAlignment;
+
+  private:
+    ImplicitAllocationArguments(Sema &SemaRef, Expr *TypeIdentityArg,
+                                Expr *SizeArg, Expr *AlignArg,
+                                bool IsMSVCCompatibilityFallback);
+
+    // Type-identity, size, and alignment
+    static constexpr unsigned MaxImplicitArguments = 3;
+    bool IsMSVCCompatibilityFallback;
+
+    unsigned ArgumentCount;
+    Expr *ImplicitArguments[MaxImplicitArguments];
+  };
+
+  struct AllocationArgumentSet {
+    bool TypeAwareViable;
+    SmallVector<ImplicitAllocationArguments, 3> Candidates;
+  };
+
+private:
+  struct ResolvedAllocation;
+
+public:
   /// 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:
@@ -8962,6 +9000,25 @@ class Sema final : public SemaBase {
   void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,
                                  bool DeleteWasArrayForm);
 
+  struct ResolvedAllocation {
+    FunctionDecl *OperatorNew;
+    FunctionDecl *OperatorDelete;
+    ImplicitAllocationParameters IAP;
+    // type-identity, size, alignment, nothrow or other single placement
+    // parameter
+    SmallVector<Expr *, 4> Arguments;
+  };
+
+  std::optional<AllocationArgumentSet>
+  resolveAllocationArguments(LookupResult &R,
+                             const ImplicitAllocationParameters &,
+                             ArrayRef<Expr *> PlacementArguments);
+  bool getTypeIdentityArgument(QualType Type, SourceLocation, Expr 
**FoundExpr);
+
+  Expr *AllocationSizeExpr = nullptr;
+  Expr *AllocationAlignmentExpr = nullptr;
+  llvm::DenseMap<QualType, Expr *> AllocationTypeIdentityArguments;
+
   ///@}
 
   //
@@ -10371,7 +10428,7 @@ class Sema final : public SemaBase {
   void DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range,
                                     DeclarationName Name,
                                     OverloadCandidateSet &CandidateSet,
-                                    FunctionDecl *Fn, MultiExprArg Args,
+                                    FunctionDecl *Fn, ArrayRef<Expr *> Args,
                                     bool IsMember = false);
 
   ExprResult InitializeExplicitObjectArgument(Sema &S, Expr *Obj,
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 7f9b1d642cf9d..4e8dbec4f35d1 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1478,13 +1478,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..f7c22c99968c6 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2438,6 +2438,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 +2455,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,
+        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 +2486,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))
@@ -2787,38 +2789,40 @@ static void 
diagnoseNoViableFunctionForAllocationOverloadResolution(
   Candidates.NoteCandidates(S, Args, Cands, "", R.getNameLoc());
 }
 
-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;
-  }
+using ImplicitAllocationArguments = Sema::ImplicitAllocationArguments;
+using AllocationArgumentSet = Sema::AllocationArgumentSet;
 
-  OverloadCandidateSet Candidates(R.getNameLoc(),
-                                  OverloadCandidateSet::CSK_Normal);
+enum class AllocatorResolveResult { Success, Retry, Error };
+static AllocatorResolveResult
+resolveAllocationOverload(Sema &S, LookupResult &R, SourceRange Range,
+                          ImplicitAllocationArguments &AllocationArgs,
+                          ArrayRef<Expr *> TrialArguments,
+                          FunctionDecl *&Operator,
+                          OverloadCandidateSet &Candidates, bool Diagnose) {
+  AllocationArgs.updateLookupForMSVCCompatibility(S, R);
+
+  bool ArgumentListIsTypeAware =
+      isTypeAwareAllocation(AllocationArgs.PassTypeIdentity);
   for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.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);
   }
 
@@ -2826,57 +2830,17 @@ static bool resolveAllocationOverloadInterior(
   OverloadCandidateSet::iterator Best;
   switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) {
   case OR_Success: {
-    // Got one!
     FunctionDecl *FnDecl = Best->Function;
     if (S.CheckAllocationAccess(R.getNameLoc(), Range, R.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) {
@@ -2884,15 +2848,16 @@ static bool resolveAllocationOverloadInterior(
           PartialDiagnosticAt(R.getNameLoc(),
                               S.PDiag(diag::err_ovl_ambiguous_call)
                                   << R.getLookupName() << Range),
-          S, OCD_AmbiguousCandidates, Args);
+          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;
+                                     Candidates, Best->Function,
+                                     TrialArguments);
+    return AllocatorResolveResult::Error;
   }
   }
   llvm_unreachable("Unreachable, bad result from BestViableFunction");
@@ -2918,55 +2883,184 @@ 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;
-  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))
+static void DiagnoseAllocationLookupFailure(
+    Sema &SemaRef, LookupResult &R, SourceRange Range,
+    std::optional<AllocationArgumentSet> &ArgumentCandidates,
+    ArrayRef<Expr *> PlacementArguments) {
+  ImplicitAllocationArguments *UnalignedArgumentList = nullptr;
+  ImplicitAllocationArguments *AlignedArgumentList = nullptr;
+  for (ImplicitAllocationArguments &AllocationArguments :
+       ArgumentCandidates->Candidates) {
+    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) {
+    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);
+}
+
+bool Sema::getTypeIdentityArgument(QualType Type, SourceLocation Loc,
+                                   Expr **FoundExpr) {
+  auto [Slot, Inserted] =
+      AllocationTypeIdentityArguments.insert({Type, nullptr});
+  if (!Inserted) {
+    if (!Slot->second)
       return true;
-    if (Operator)
-      return false;
+    *FoundExpr = Slot->second;
+    return false;
+  }
+  QualType TypeIdentity = tryBuildStdTypeIdentity(Type, SourceLocation());
+  if (TypeIdentity.isNull())
+    return false;
 
-    // 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) {
+  if (RequireCompleteType(Loc, TypeIdentity, diag::err_incomplete_type))
+    return true;
+  Expr *TypeIdentityArgument =
+      new (Context) CXXScalarValueInitExpr(TypeIdentity, nullptr, Loc);
+  Slot->second = TypeIdentityArgument;
+  *FoundExpr = TypeIdentityArgument;
+  return false;
+}
+
+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;
+  }
+}
+
+void ImplicitAllocationArguments::updateLookupForMSVCCompatibility(
+    Sema &S, LookupResult &R) {
+  if (!IsMSVCCompatibilityFallback)
+    return;
+  // 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.
+  R.clear();
+  R.setLookupName(S.Context.DeclarationNames.getCXXOperatorName(OO_New));
+  // FIXME: This will give bad diagnostics pointing at the wrong functions.
+  S.LookupQualifiedName(R, S.Context.getTranslationUnitDecl());
+}
+
+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 (EnumDecl *StdAlignValT = getStdAlignValT()) {
+      QualType AlignValT = Context.getCanonicalTagType(StdAlignValT);
+      AllocationAlignmentExpr = new (Context)
+          CXXScalarValueInitExpr(AlignValT, nullptr, SourceLocation());
+    }
+  }
+
+  AllocationArgumentSet FoundArguments = {
+      isTypeAwareAllocation(IAP.PassTypeIdentity), {}};
+  if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
+    Expr *TypeIdentityArgument = nullptr;
+    if (getTypeIdentityArgument(IAP.Type, R.getNameLoc(),
+                                &TypeIdentityArgument))
+      return std::nullopt;
+    if (TypeIdentityArgument) {
+      Expr *AlignmentExpr = AllocationAlignmentExpr;
+      if (!PlacementArguments.empty() &&
+          PlacementArguments.front()->getType()->isAlignValT())
+        AlignmentExpr = nullptr;
+      FoundArguments.Candidates.push_back(ImplicitAllocationArguments(
+          *this, TypeIdentityArgument, AllocationSizeExpr, AlignmentExpr,
+          /*IsMSVCCompatibilityFallback=*/false));
+    } else
+      FoundArguments.TypeAwareViable = 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.Candidates.push_back(AlignedArguments);
+  FoundArguments.Candidates.push_back(UnalignedArguments);
+
+  // The MSVC global fallback path
+  if (getLangOpts().MSVCCompat &&
+      R.getLookupName().getCXXOverloadedOperator() == OO_Array_New)
+    FoundArguments.Candidates.push_back(ImplicitAllocationArguments(
+        *this, /*TypeIdentityArg=*/nullptr, AllocationSizeExpr,
+        /*AlignArg=*/nullptr, /*IsMSVCCompatibilityFallback=*/true));
+  return FoundArguments;
+}
+
+std::optional<Sema::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 +3073,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 +3084,11 @@ 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);
+  bool ConsideredTypeAwareAllocation = false;
+  ResolvedAllocation Result = {/*OperatorNew=*/nullptr,
+                               /*OperatorDelete=*/nullptr,
+                               RequestedIAP,
+                               {}};
 
   // Find the allocation function.
   {
@@ -3053,14 +3106,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 +3124,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 +3133,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> Candidates =
+        resolveAllocationArguments(R, RequestedIAP, PlaceArgs);
+    if (!Candidates)
+      return std::nullopt;
+    ConsideredTypeAwareAllocation = Candidates->TypeAwareViable;
+
+    for (ImplicitAllocationArguments &ArgumentList : Candidates->Candidates) {
+      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, Candidates, 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 +3195,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 +3219,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,16 +3233,16 @@ 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 LookupMode = ConsideredTypeAwareAllocation
                                        ? DeallocLookupMode::OptionallyTyped
                                        : DeallocLookupMode::Untyped;
     LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
@@ -3188,7 +3268,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 +3292,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 +3335,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 +3349,8 @@ 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,
+        typeAwareAllocationModeFromBool(ConsideredTypeAwareAllocation),
         alignedAllocationModeFromBool(
             hasNewExtendedAlignment(*this, AllocElemType)),
         sizedDeallocationModeFromBool(FoundGlobalDelete)};
@@ -3290,7 +3371,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 +3430,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 +3445,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 +3459,7 @@ bool Sema::FindAllocationFunctions(
            diag::note_member_declared_here) << DeleteName;
   }
 
-  return false;
+  return Result;
 }
 
 void Sema::DeclareGlobalNewDelete() {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index eafda32198f11..067f3f51c7f41 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -17375,7 +17375,7 @@ bool clang::shouldEnforceArgLimit(bool 
PartialOverloading,
 void Sema::DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range,
                                         DeclarationName Name,
                                         OverloadCandidateSet &CandidateSet,
-                                        FunctionDecl *Fn, MultiExprArg Args,
+                                        FunctionDecl *Fn, ArrayRef<Expr *> 
Args,
                                         bool IsMember) {
   StringLiteral *Msg = Fn->getDeletedMessage();
   CandidateSet.NoteCandidates(
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..fde63f35bb9e2
--- /dev/null
+++ b/clang/test/SemaCXX/microsoft-new-array-fallback.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -fms-compatibility -fsyntax-only -verify -std=c++11 %s
+
+typedef __SIZE_TYPE__ size_t;
+
+void *operator new(size_t); // #new_decl
+
+struct Tag {};
+
+void f() {
+  int *p = new (Tag{}) int[4];
+  // expected-error@-1 {{no matching function for call to 'operator new'}}
+  // expected-note@#new_decl {{candidate function not viable: requires 1 
argument, but 2 were provided}}
+  (void)p;
+}
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..caf1821c0e3f2 100644
--- a/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
+++ b/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
@@ -7,7 +7,6 @@
 
 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>
@@ -57,3 +56,17 @@ void f() {
   TestType *t = new TestType;
   delete t;
 }
+
+#if !defined(INVALID_TYPE_IDENTITY_VERSION)
+struct Bad {};
+template <> struct std::type_identity<Bad>; // #incomplete_specialization
+
+// 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;
+}
+#endif

>From 727c182002c6d7d521791d7ececa07af020ff1dc Mon Sep 17 00:00:00 2001
From: Oliver Hunt <[email protected]>
Date: Thu, 23 Jul 2026 13:17:10 -0700
Subject: [PATCH 2/4] Update for reviewer feedback

---
 .../Sema/DynamicAllocationArgumentsCXX.h      | 72 +++++++++++++++++++
 clang/include/clang/Sema/Sema.h               | 52 +-------------
 clang/lib/Sema/SemaCoroutine.cpp              |  1 +
 clang/lib/Sema/SemaExprCXX.cpp                |  9 +--
 clang/lib/Sema/SemaOverload.cpp               |  2 +-
 5 files changed, 80 insertions(+), 56 deletions(-)
 create mode 100644 clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h

diff --git a/clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h 
b/clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h
new file mode 100644
index 0000000000000..c3ee16382dcb6
--- /dev/null
+++ b/clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h
@@ -0,0 +1,72 @@
+//===- 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;
+  }
+
+  void updateLookupForMSVCCompatibility(Sema &, LookupResult &);
+  TypeAwareAllocationMode PassTypeIdentity;
+  AlignedAllocationMode PassAlignment;
+
+private:
+  ImplicitAllocationArguments(Sema &SemaRef, Expr *TypeIdentityArg,
+                              Expr *SizeArg, Expr *AlignArg,
+                              bool IsMSVCCompatibilityFallback);
+
+  // Type-identity, size, and alignment
+  static constexpr unsigned MaxImplicitArguments = 3;
+  bool IsMSVCCompatibilityFallback;
+
+  unsigned ArgumentCount;
+  Expr *ImplicitArguments[MaxImplicitArguments];
+};
+
+struct AllocationArgumentSet {
+  bool TypeAwareViable;
+  SmallVector<ImplicitAllocationArguments, 3> Candidates;
+};
+
+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 292c53f460826..963065b86a1d0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -116,6 +116,7 @@ struct InlineAsmIdentifierInfo;
 
 namespace clang {
 class ADLResult;
+struct AllocationArgumentSet;
 class APValue;
 struct ASTConstraintSatisfaction;
 class ASTConsumer;
@@ -157,6 +158,7 @@ enum class OverloadCandidateParamOrder : char;
 enum OverloadCandidateRewriteKind : unsigned;
 class OverloadCandidateSet;
 class Preprocessor;
+struct ResolvedAllocation;
 class SemaAMDGPU;
 class SemaARM;
 class SemaAVR;
@@ -8651,45 +8653,6 @@ class Sema final : public SemaBase {
   bool CheckAllocatedType(QualType AllocType, SourceLocation Loc,
                           SourceRange R);
 
-  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;
-    }
-
-    void updateLookupForMSVCCompatibility(Sema &, LookupResult &);
-    TypeAwareAllocationMode PassTypeIdentity;
-    AlignedAllocationMode PassAlignment;
-
-  private:
-    ImplicitAllocationArguments(Sema &SemaRef, Expr *TypeIdentityArg,
-                                Expr *SizeArg, Expr *AlignArg,
-                                bool IsMSVCCompatibilityFallback);
-
-    // Type-identity, size, and alignment
-    static constexpr unsigned MaxImplicitArguments = 3;
-    bool IsMSVCCompatibilityFallback;
-
-    unsigned ArgumentCount;
-    Expr *ImplicitArguments[MaxImplicitArguments];
-  };
-
-  struct AllocationArgumentSet {
-    bool TypeAwareViable;
-    SmallVector<ImplicitAllocationArguments, 3> Candidates;
-  };
-
-private:
-  struct ResolvedAllocation;
-
-public:
   /// Finds the overloads of operator new and delete that are appropriate
   /// for the allocation.
   std::optional<ResolvedAllocation> FindAllocationFunctions(
@@ -9000,15 +8963,6 @@ class Sema final : public SemaBase {
   void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,
                                  bool DeleteWasArrayForm);
 
-  struct ResolvedAllocation {
-    FunctionDecl *OperatorNew;
-    FunctionDecl *OperatorDelete;
-    ImplicitAllocationParameters IAP;
-    // type-identity, size, alignment, nothrow or other single placement
-    // parameter
-    SmallVector<Expr *, 4> Arguments;
-  };
-
   std::optional<AllocationArgumentSet>
   resolveAllocationArguments(LookupResult &R,
                              const ImplicitAllocationParameters &,
@@ -10428,7 +10382,7 @@ class Sema final : public SemaBase {
   void DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range,
                                     DeclarationName Name,
                                     OverloadCandidateSet &CandidateSet,
-                                    FunctionDecl *Fn, ArrayRef<Expr *> Args,
+                                    FunctionDecl *Fn, MultiExprArg Args,
                                     bool IsMember = false);
 
   ExprResult InitializeExplicitObjectArgument(Sema &S, Expr *Obj,
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 4e8dbec4f35d1..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"
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f7c22c99968c6..0079a488c13c1 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"
@@ -2789,15 +2790,11 @@ static void 
diagnoseNoViableFunctionForAllocationOverloadResolution(
   Candidates.NoteCandidates(S, Args, Cands, "", R.getNameLoc());
 }
 
-using ImplicitAllocationArguments = Sema::ImplicitAllocationArguments;
-using AllocationArgumentSet = Sema::AllocationArgumentSet;
-
 enum class AllocatorResolveResult { Success, Retry, Error };
 static AllocatorResolveResult
 resolveAllocationOverload(Sema &S, LookupResult &R, SourceRange Range,
                           ImplicitAllocationArguments &AllocationArgs,
-                          ArrayRef<Expr *> TrialArguments,
-                          FunctionDecl *&Operator,
+                          MultiExprArg TrialArguments, FunctionDecl *&Operator,
                           OverloadCandidateSet &Candidates, bool Diagnose) {
   AllocationArgs.updateLookupForMSVCCompatibility(S, R);
 
@@ -3054,7 +3051,7 @@ Sema::resolveAllocationArguments(LookupResult &R,
   return FoundArguments;
 }
 
-std::optional<Sema::ResolvedAllocation>
+std::optional<ResolvedAllocation>
 Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
                               AllocationFunctionScope NewScope,
                               AllocationFunctionScope DeleteScope,
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 067f3f51c7f41..eafda32198f11 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -17375,7 +17375,7 @@ bool clang::shouldEnforceArgLimit(bool 
PartialOverloading,
 void Sema::DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range,
                                         DeclarationName Name,
                                         OverloadCandidateSet &CandidateSet,
-                                        FunctionDecl *Fn, ArrayRef<Expr *> 
Args,
+                                        FunctionDecl *Fn, MultiExprArg Args,
                                         bool IsMember) {
   StringLiteral *Msg = Fn->getDeletedMessage();
   CandidateSet.NoteCandidates(

>From 57bdce15a0a723e87c086fd64d4a5803bcaf3e5f Mon Sep 17 00:00:00 2001
From: Oliver Hunt <[email protected]>
Date: Thu, 23 Jul 2026 15:10:56 -0700
Subject: [PATCH 3/4] sigh, missed this

---
 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 0079a488c13c1..fad2b99a70a3d 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2461,7 +2461,7 @@ ExprResult Sema::BuildCXXNew(SourceRange Range, bool 
UseGlobal,
       !Expr::hasAnyTypeDependentArguments(PlacementArgs)) {
     auto FoundAllocation = FindAllocationFunctions(
         StartLoc, AllocationParameterRange, Scope, Scope, AllocType,
-        ArraySize.has_value(), IAP, PlacementArgs);
+        /*IsArray=*/ArraySize.has_value(), IAP, PlacementArgs);
     if (!FoundAllocation)
       return ExprError();
     IAP = FoundAllocation->IAP;

>From 2e695908b66d263b44be81785913b4d0c0e77adb Mon Sep 17 00:00:00 2001
From: Oliver Hunt <[email protected]>
Date: Sun, 26 Jul 2026 18:11:11 -0700
Subject: [PATCH 4/4] Stop mutating the initial LookupResult, and instead use
 an optional buffer for the MSVC fallback.

The refactoring actually removed the need for a the old "did we try type
aware allocators", and a few other things also got cleaned up.
---
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 .../Sema/DynamicAllocationArgumentsCXX.h      |  12 +-
 clang/include/clang/Sema/Sema.h               |  12 +-
 clang/lib/Sema/SemaExprCXX.cpp                | 171 ++++++++++--------
 .../SemaCXX/microsoft-new-array-fallback.cpp  |   5 +-
 .../type-aware-new-invalid-type-identity.cpp  |   2 +
 6 files changed, 114 insertions(+), 91 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 371ad4535e95f..ef1478b15c9fc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5524,6 +5524,9 @@ def err_no_viable_destructor : Error<
 def err_ambiguous_destructor : Error<
   "destructor of class %0 is ambiguous">;
 
+def note_ovl_msvc_allocation_fallback_failed : Note<
+  "MSVC compatibility fall back to %0 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
index c3ee16382dcb6..7ce800111dfe6 100644
--- a/clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h
+++ b/clang/include/clang/Sema/DynamicAllocationArgumentsCXX.h
@@ -36,9 +36,12 @@ struct ImplicitAllocationArguments {
     return nullptr;
   }
 
-  void updateLookupForMSVCCompatibility(Sema &, LookupResult &);
+  const LookupResult &
+  updateLookupForMSVCCompatibility(Sema &, const LookupResult &,
+                                   std::optional<LookupResult> &) const;
   TypeAwareAllocationMode PassTypeIdentity;
   AlignedAllocationMode PassAlignment;
+  bool IsMSVCCompatibilityFallback;
 
 private:
   ImplicitAllocationArguments(Sema &SemaRef, Expr *TypeIdentityArg,
@@ -47,17 +50,10 @@ struct ImplicitAllocationArguments {
 
   // Type-identity, size, and alignment
   static constexpr unsigned MaxImplicitArguments = 3;
-  bool IsMSVCCompatibilityFallback;
-
   unsigned ArgumentCount;
   Expr *ImplicitArguments[MaxImplicitArguments];
 };
 
-struct AllocationArgumentSet {
-  bool TypeAwareViable;
-  SmallVector<ImplicitAllocationArguments, 3> Candidates;
-};
-
 struct ResolvedAllocation {
   FunctionDecl *OperatorNew;
   FunctionDecl *OperatorDelete;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 963065b86a1d0..4ed05a183a70b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -116,7 +116,6 @@ struct InlineAsmIdentifierInfo;
 
 namespace clang {
 class ADLResult;
-struct AllocationArgumentSet;
 class APValue;
 struct ASTConstraintSatisfaction;
 class ASTConsumer;
@@ -138,6 +137,7 @@ struct DeductionFailureInfo;
 class DependentDiagnostic;
 class Designation;
 class IdentifierInfo;
+struct ImplicitAllocationArguments;
 class ImplicitConversionSequence;
 typedef MutableArrayRef<ImplicitConversionSequence> ConversionSequenceList;
 class InitializationKind;
@@ -226,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);
@@ -8967,7 +8971,11 @@ class Sema final : public SemaBase {
   resolveAllocationArguments(LookupResult &R,
                              const ImplicitAllocationParameters &,
                              ArrayRef<Expr *> PlacementArguments);
-  bool getTypeIdentityArgument(QualType Type, SourceLocation, Expr 
**FoundExpr);
+
+  // Attempts to construct the type identity argument for the call to a
+  // type aware operator new. In the event of an error this returns
+  // std::nullopt.
+  std::optional<Expr *> getTypeIdentityArgument(QualType Type, SourceLocation);
 
   Expr *AllocationSizeExpr = nullptr;
   Expr *AllocationAlignmentExpr = nullptr;
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index fad2b99a70a3d..796244a1eab8a 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2726,9 +2726,10 @@ bool Sema::CheckAllocatedType(QualType AllocType, 
SourceLocation Loc,
 }
 
 static void diagnoseNoViableFunctionForAllocationOverloadResolution(
-    Sema &S, LookupResult &R, SourceRange Range, ArrayRef<Expr *> Args,
-    OverloadCandidateSet &Candidates, OverloadCandidateSet *AlignedCandidates,
-    Expr *AlignArg) {
+    Sema &S, const LookupResult &R,
+    const std::optional<LookupResult> &MSVCFallback, SourceRange Range,
+    ArrayRef<Expr *> Args, OverloadCandidateSet &Candidates,
+    OverloadCandidateSet *AlignedCandidates, Expr *AlignArg) {
   // 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.
@@ -2788,19 +2789,27 @@ static void 
diagnoseNoViableFunctionForAllocationOverloadResolution(
     AlignedCandidates->NoteCandidates(S, AlignedArgs, AlignedCands, "",
                                       R.getNameLoc());
   Candidates.NoteCandidates(S, Args, Cands, "", R.getNameLoc());
+  if (MSVCFallback)
+    S.Diag(MSVCFallback->getNameLoc(),
+           diag::note_ovl_msvc_allocation_fallback_failed)
+        << MSVCFallback->getLookupName() << Range;
 }
 
 enum class AllocatorResolveResult { Success, Retry, Error };
-static AllocatorResolveResult
-resolveAllocationOverload(Sema &S, LookupResult &R, SourceRange Range,
-                          ImplicitAllocationArguments &AllocationArgs,
-                          MultiExprArg TrialArguments, FunctionDecl *&Operator,
-                          OverloadCandidateSet &Candidates, bool Diagnose) {
-  AllocationArgs.updateLookupForMSVCCompatibility(S, R);
+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 = R.begin(), AllocEnd = R.end();
+
+  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.
@@ -2825,10 +2834,11 @@ resolveAllocationOverload(Sema &S, LookupResult &R, 
SourceRange Range,
 
   // Do the resolution.
   OverloadCandidateSet::iterator Best;
-  switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) {
+  switch (Candidates.BestViableFunction(S, LocalLookup.getNameLoc(), Best)) {
   case OR_Success: {
     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 AllocatorResolveResult::Error;
 
@@ -2842,18 +2852,18 @@ resolveAllocationOverload(Sema &S, LookupResult &R, 
SourceRange Range,
   case OR_Ambiguous:
     if (Diagnose) {
       Candidates.NoteCandidates(
-          PartialDiagnosticAt(R.getNameLoc(),
+          PartialDiagnosticAt(LocalLookup.getNameLoc(),
                               S.PDiag(diag::err_ovl_ambiguous_call)
-                                  << R.getLookupName() << Range),
+                                  << LocalLookup.getLookupName() << Range),
           S, OCD_AmbiguousCandidates, TrialArguments);
     }
     return AllocatorResolveResult::Error;
 
   case OR_Deleted: {
     if (Diagnose)
-      S.DiagnoseUseOfDeletedFunction(R.getNameLoc(), Range, R.getLookupName(),
-                                     Candidates, Best->Function,
-                                     TrialArguments);
+      S.DiagnoseUseOfDeletedFunction(LocalLookup.getNameLoc(), Range,
+                                     LocalLookup.getLookupName(), Candidates,
+                                     Best->Function, TrialArguments);
     return AllocatorResolveResult::Error;
   }
   }
@@ -2880,14 +2890,15 @@ static void LookupGlobalDeallocationFunctions(Sema &S, 
SourceLocation Loc,
   }
 }
 
-static void DiagnoseAllocationLookupFailure(
-    Sema &SemaRef, LookupResult &R, SourceRange Range,
-    std::optional<AllocationArgumentSet> &ArgumentCandidates,
-    ArrayRef<Expr *> PlacementArguments) {
+static void
+DiagnoseAllocationLookupFailure(Sema &SemaRef, const LookupResult &BaseLookup,
+                                SourceRange Range,
+                                AllocationArgumentSet &ArgumentCandidates,
+                                ArrayRef<Expr *> PlacementArguments) {
+  std::optional<LookupResult> MSVCFallback;
   ImplicitAllocationArguments *UnalignedArgumentList = nullptr;
   ImplicitAllocationArguments *AlignedArgumentList = nullptr;
-  for (ImplicitAllocationArguments &AllocationArguments :
-       ArgumentCandidates->Candidates) {
+  for (ImplicitAllocationArguments &AllocationArguments : ArgumentCandidates) {
     if (AllocationArguments.PassTypeIdentity == TypeAwareAllocationMode::Yes)
       continue;
     if (AllocationArguments.PassAlignment == AlignedAllocationMode::Yes)
@@ -2904,50 +2915,52 @@ static void DiagnoseAllocationLookupFailure(
   auto Rerun = [&](ImplicitAllocationArguments &ArgumentList,
                    OverloadCandidateSet &Candidates,
                    SmallVectorImpl<Expr *> &Args) {
+    const LookupResult &LocalLookup =
+        ArgumentList.updateLookupForMSVCCompatibility(SemaRef, BaseLookup,
+                                                      MSVCFallback);
     llvm::append_range(Args, ArgumentList.getImplicitArguments());
     llvm::append_range(Args, PlacementArguments);
     FunctionDecl *Unused = nullptr;
-    resolveAllocationOverload(SemaRef, R, Range, ArgumentList, Args, Unused,
-                              Candidates, /*Diagnose=*/false);
+    resolveAllocationOverload(SemaRef, LocalLookup, Range, ArgumentList, Args,
+                              Unused, Candidates, /*Diagnose=*/false);
   };
   std::optional<OverloadCandidateSet> AlignedCandidates;
   Expr *AlignArg = nullptr;
   if (AlignedArgumentList) {
-    AlignedCandidates.emplace(R.getNameLoc(), 
OverloadCandidateSet::CSK_Normal);
+    AlignedCandidates.emplace(BaseLookup.getNameLoc(),
+                              OverloadCandidateSet::CSK_Normal);
     SmallVector<Expr *, 4> AlignedArgs;
     Rerun(*AlignedArgumentList, *AlignedCandidates, AlignedArgs);
     AlignArg = AlignedArgumentList->getAlignmentArgument();
   }
-  OverloadCandidateSet UnalignedCandidates(R.getNameLoc(),
+  OverloadCandidateSet UnalignedCandidates(BaseLookup.getNameLoc(),
                                            OverloadCandidateSet::CSK_Normal);
   SmallVector<Expr *, 4> UnalignedArgs;
   Rerun(*UnalignedArgumentList, UnalignedCandidates, UnalignedArgs);
   diagnoseNoViableFunctionForAllocationOverloadResolution(
-      SemaRef, R, Range, UnalignedArgs, UnalignedCandidates,
-      AlignedCandidates ? &*AlignedCandidates : nullptr, AlignArg);
+      SemaRef, BaseLookup, MSVCFallback, Range, UnalignedArgs,
+      UnalignedCandidates, AlignedCandidates ? &*AlignedCandidates : nullptr,
+      AlignArg);
 }
 
-bool Sema::getTypeIdentityArgument(QualType Type, SourceLocation Loc,
-                                   Expr **FoundExpr) {
+std::optional<Expr *> Sema::getTypeIdentityArgument(QualType Type,
+                                                    SourceLocation Loc) {
   auto [Slot, Inserted] =
       AllocationTypeIdentityArguments.insert({Type, nullptr});
-  if (!Inserted) {
-    if (!Slot->second)
-      return true;
-    *FoundExpr = Slot->second;
-    return false;
-  }
-  QualType TypeIdentity = tryBuildStdTypeIdentity(Type, SourceLocation());
-  if (TypeIdentity.isNull())
-    return false;
 
-  if (RequireCompleteType(Loc, TypeIdentity, diag::err_incomplete_type))
-    return true;
+  if (!Inserted)
+    return Slot->second;
+
+  QualType TypeIdentity = tryBuildStdTypeIdentity(Type, SourceLocation());
+  if (TypeIdentity.isNull() ||
+      RequireCompleteType(Loc, TypeIdentity, diag::err_incomplete_type)) {
+    AllocationTypeIdentityArguments.erase(Slot);
+    return std::nullopt;
+  }
   Expr *TypeIdentityArgument =
       new (Context) CXXScalarValueInitExpr(TypeIdentity, nullptr, Loc);
   Slot->second = TypeIdentityArgument;
-  *FoundExpr = TypeIdentityArgument;
-  return false;
+  return TypeIdentityArgument;
 }
 
 ImplicitAllocationArguments::ImplicitAllocationArguments(
@@ -2971,20 +2984,23 @@ 
ImplicitAllocationArguments::ImplicitAllocationArguments(
   }
 }
 
-void ImplicitAllocationArguments::updateLookupForMSVCCompatibility(
-    Sema &S, LookupResult &R) {
+const LookupResult &
+ImplicitAllocationArguments::updateLookupForMSVCCompatibility(
+    Sema &S, const LookupResult &BaseLookup,
+    std::optional<LookupResult> &Buffer) const {
   if (!IsMSVCCompatibilityFallback)
-    return;
+    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.
-  R.clear();
-  R.setLookupName(S.Context.DeclarationNames.getCXXOperatorName(OO_New));
+  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(R, S.Context.getTranslationUnitDecl());
+  S.LookupQualifiedName(Fallback, S.Context.getTranslationUnitDecl());
+  return Fallback;
 }
 
 std::optional<AllocationArgumentSet>
@@ -3008,23 +3024,21 @@ Sema::resolveAllocationArguments(LookupResult &R,
     }
   }
 
-  AllocationArgumentSet FoundArguments = {
-      isTypeAwareAllocation(IAP.PassTypeIdentity), {}};
+  AllocationArgumentSet FoundArguments;
   if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
-    Expr *TypeIdentityArgument = nullptr;
-    if (getTypeIdentityArgument(IAP.Type, R.getNameLoc(),
-                                &TypeIdentityArgument))
+    std::optional<Expr *> TypeIdentityArgument =
+        getTypeIdentityArgument(IAP.Type, R.getNameLoc());
+    if (!TypeIdentityArgument)
       return std::nullopt;
-    if (TypeIdentityArgument) {
-      Expr *AlignmentExpr = AllocationAlignmentExpr;
-      if (!PlacementArguments.empty() &&
-          PlacementArguments.front()->getType()->isAlignValT())
-        AlignmentExpr = nullptr;
-      FoundArguments.Candidates.push_back(ImplicitAllocationArguments(
-          *this, TypeIdentityArgument, AllocationSizeExpr, AlignmentExpr,
-          /*IsMSVCCompatibilityFallback=*/false));
-    } else
-      FoundArguments.TypeAwareViable = false;
+
+    assert(*TypeIdentityArgument);
+    Expr *AlignmentExpr = AllocationAlignmentExpr;
+    if (!PlacementArguments.empty() &&
+        PlacementArguments.front()->getType()->isAlignValT())
+      AlignmentExpr = nullptr;
+    FoundArguments.push_back(ImplicitAllocationArguments(
+        *this, *TypeIdentityArgument, AllocationSizeExpr, AlignmentExpr,
+        /*IsMSVCCompatibilityFallback=*/false));
   }
 
   ImplicitAllocationArguments UnalignedArguments(
@@ -3039,13 +3053,13 @@ Sema::resolveAllocationArguments(LookupResult &R,
   //   new-extended alignment, the alignment argument is removed from the
   //   argument list, and overload resolution is performed again.
   if (IAP.PassAlignment == AlignedAllocationMode::Yes)
-    FoundArguments.Candidates.push_back(AlignedArguments);
-  FoundArguments.Candidates.push_back(UnalignedArguments);
+    FoundArguments.push_back(AlignedArguments);
+  FoundArguments.push_back(UnalignedArguments);
 
   // The MSVC global fallback path
   if (getLangOpts().MSVCCompat &&
       R.getLookupName().getCXXOverloadedOperator() == OO_Array_New)
-    FoundArguments.Candidates.push_back(ImplicitAllocationArguments(
+    FoundArguments.push_back(ImplicitAllocationArguments(
         *this, /*TypeIdentityArg=*/nullptr, AllocationSizeExpr,
         /*AlignArg=*/nullptr, /*IsMSVCCompatibilityFallback=*/true));
   return FoundArguments;
@@ -3081,7 +3095,6 @@ Sema::FindAllocationFunctions(SourceLocation StartLoc, 
SourceRange Range,
 
   QualType AllocElemType = Context.getBaseElementType(AllocType);
 
-  bool ConsideredTypeAwareAllocation = false;
   ResolvedAllocation Result = {/*OperatorNew=*/nullptr,
                                /*OperatorDelete=*/nullptr,
                                RequestedIAP,
@@ -3130,13 +3143,12 @@ Sema::FindAllocationFunctions(SourceLocation StartLoc, 
SourceRange Range,
     // We do our own custom access checks below.
     R.suppressDiagnostics();
 
-    std::optional<AllocationArgumentSet> Candidates =
+    std::optional<AllocationArgumentSet> ArgumentListCandidates =
         resolveAllocationArguments(R, RequestedIAP, PlaceArgs);
-    if (!Candidates)
+    if (!ArgumentListCandidates)
       return std::nullopt;
-    ConsideredTypeAwareAllocation = Candidates->TypeAwareViable;
 
-    for (ImplicitAllocationArguments &ArgumentList : Candidates->Candidates) {
+    for (ImplicitAllocationArguments &ArgumentList : *ArgumentListCandidates) {
       SmallVector<Expr *, 4> TrialArguments(
           ArgumentList.getImplicitArguments());
       llvm::append_range(TrialArguments, PlaceArgs);
@@ -3159,7 +3171,8 @@ Sema::FindAllocationFunctions(SourceLocation StartLoc, 
SourceRange Range,
       }
     }
     if (Diagnose)
-      DiagnoseAllocationLookupFailure(*this, R, Range, Candidates, PlaceArgs);
+      DiagnoseAllocationLookupFailure(*this, R, Range, *ArgumentListCandidates,
+                                      PlaceArgs);
     return std::nullopt;
   }
 foundCandidate:
@@ -3239,9 +3252,10 @@ Sema::FindAllocationFunctions(SourceLocation StartLoc, 
SourceRange Range,
       return std::nullopt;
 
     DeclareGlobalNewDelete();
-    DeallocLookupMode LookupMode = ConsideredTypeAwareAllocation
-                                       ? DeallocLookupMode::OptionallyTyped
-                                       : DeallocLookupMode::Untyped;
+    DeallocLookupMode LookupMode =
+        isTypeAwareAllocation(RequestedIAP.PassTypeIdentity)
+            ? DeallocLookupMode::OptionallyTyped
+            : DeallocLookupMode::Untyped;
     LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
                                       DeleteName);
   }
@@ -3346,8 +3360,7 @@ Sema::FindAllocationFunctions(SourceLocation StartLoc, 
SourceRange Range,
     // with a size_t where possible (which it always is in this case).
     llvm::SmallVector<UsualDeallocFnInfo, 4> BestDeallocFns;
     ImplicitDeallocationParameters IDP = {
-        AllocElemType,
-        typeAwareAllocationModeFromBool(ConsideredTypeAwareAllocation),
+        AllocElemType, RequestedIAP.PassTypeIdentity,
         alignedAllocationModeFromBool(
             hasNewExtendedAlignment(*this, AllocElemType)),
         sizedDeallocationModeFromBool(FoundGlobalDelete)};
diff --git a/clang/test/SemaCXX/microsoft-new-array-fallback.cpp 
b/clang/test/SemaCXX/microsoft-new-array-fallback.cpp
index fde63f35bb9e2..80bb65363f828 100644
--- a/clang/test/SemaCXX/microsoft-new-array-fallback.cpp
+++ b/clang/test/SemaCXX/microsoft-new-array-fallback.cpp
@@ -7,8 +7,9 @@ void *operator new(size_t); // #new_decl
 struct Tag {};
 
 void f() {
-  int *p = new (Tag{}) int[4];
-  // expected-error@-1 {{no matching function for call to 'operator new'}}
+  int *p = new (Tag{}) int[4]; // #new_expr
+  // expected-error@#new_expr {{no matching function for call to 'operator 
new[]'}}
   // expected-note@#new_decl {{candidate function not viable: requires 1 
argument, but 2 were provided}}
+  // expected-note@#new_expr {{MSVC compatibility fall back to 'operator new' 
failed}}
   (void)p;
 }
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 caf1821c0e3f2..1696466eae00a 100644
--- a/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
+++ b/clang/test/SemaCXX/type-aware-new-invalid-type-identity.cpp
@@ -68,5 +68,7 @@ void failedTypeIdentitySpecialization() {
   // 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