https://github.com/ZacharyHenkel updated 
https://github.com/llvm/llvm-project/pull/225913

>From ec139c9b021253a6ea8da282da79a132ccbbaba0 Mon Sep 17 00:00:00 2001
From: Shivan Goyal <[email protected]>
Date: Wed, 23 Sep 2026 12:58:35 -0700
Subject: [PATCH 1/7] [clang] Support enum typedef names in Microsoft
 compatibility mode

**AI-authored commit message** - Upstream adaptation, analysis, code changes, 
tests, and wording were produced by an AI agent on zahen's behalf.

Accept elaborated enum specifiers that resolve to enum typedefs in supported 
non-parameter contexts when Microsoft compatibility mode is enabled.

Preserve typedef sugar in the AST, rebuild dependent qualified uses during 
template instantiation, retain access and availability diagnostics, and 
diagnose the extension through -Wmicrosoft-enum-typedef. Function parameter 
declarations remain on the standard path.

Original implementation by Shivan Goyal <[email protected]> (@shivanshu3).

Co-authored-by: Copilot <[email protected]>
Copilot-Session: f1668d1f-2a77-45bf-87ce-93f4647a5386
---
 clang/docs/MSVCCompatibility.md               |  11 +
 clang/docs/ReleaseNotes.md                    |   6 +
 clang/include/clang/Basic/DiagnosticGroups.td |  10 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   4 +
 clang/include/clang/Sema/Sema.h               |  45 ++-
 clang/lib/Parse/ParseDecl.cpp                 |  22 +-
 clang/lib/Sema/SemaDecl.cpp                   |  65 +++-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |   2 +
 clang/lib/Sema/TreeTransform.h                |  13 +
 clang/test/AST/ms-enum-typedef.cpp            |  39 +++
 clang/test/Sema/ms-enum-typedef.c             |   9 +
 .../test/SemaCXX/ms-enum-typedef-warning.cpp  |  12 +
 clang/test/SemaCXX/ms-enum-typedef.cpp        | 301 ++++++++++++++++++
 13 files changed, 518 insertions(+), 21 deletions(-)
 create mode 100644 clang/test/AST/ms-enum-typedef.cpp
 create mode 100644 clang/test/Sema/ms-enum-typedef.c
 create mode 100644 clang/test/SemaCXX/ms-enum-typedef-warning.cpp
 create mode 100644 clang/test/SemaCXX/ms-enum-typedef.cpp

diff --git a/clang/docs/MSVCCompatibility.md b/clang/docs/MSVCCompatibility.md
index e9b3f0d094df6..210dcfdf93bf0 100644
--- a/clang/docs/MSVCCompatibility.md
+++ b/clang/docs/MSVCCompatibility.md
@@ -138,6 +138,17 @@ As of this writing, Clang is able to compile a simple ATL 
hello world
 application. There are still issues parsing WRL headers for modern Windows 8
 apps, but they should be addressed soon.
 
+## Enum typedef names
+
+MSVC accepts an elaborated `enum` specifier that names a typedef for an
+enumeration. The name may be found by unqualified lookup in class scope or
+written with a possibly dependent qualifier. Clang accepts this in C++ under
+`-fms-compatibility` outside function parameter declarations and diagnoses it
+with `-Wmicrosoft-enum-typedef`. An unqualified use in a declaration whose
+`friend` specifier precedes it remains ill-formed. MSVC treats an unqualified
+name introduced by a namespace using-declaration, or found only through a
+using-directive, as declaring a new enum tag; Clang rejects such uses.
+
 ## \_\_forceinline behavior
 
 `__forceinline` behaves like `[[clang::always_inline]]`.
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index b82a8b4e00b44..95ec92a7dbb0c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -864,6 +864,12 @@ features cannot lower the translation-unit ABI level;
 
 #### Windows Support
 
+- In C++ with Microsoft compatibility mode, Clang now accepts an elaborated
+  ``enum`` specifier that names a typedef for an enumeration, found either by
+  unqualified lookup in class scope or through a possibly dependent qualifier,
+  except in function parameter declarations. This is diagnosed by
+  ``-Wmicrosoft-enum-typedef``.
+
 - Clang now accepts ``_except`` as an alias for ``__except`` in SEH handler
   position when ``-fms-compatibility`` is enabled, matching the existing
   ``_try``, ``_finally``, and ``_leave`` aliases. ``_except`` remains an 
ordinary
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 1da7698944b24..10c2b3a977d65 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1680,6 +1680,7 @@ def MicrosoftInconsistentDllImport : 
DiagGroup<"inconsistent-dllimport">;
 def MicrosoftRedeclareStatic : DiagGroup<"microsoft-redeclare-static">;
 def MicrosoftEnumForwardReference :
     DiagGroup<"microsoft-enum-forward-reference">;
+def MicrosoftEnumTypedef : DiagGroup<"microsoft-enum-typedef">;
 def MicrosoftGoto : DiagGroup<"microsoft-goto">;
 def MicrosoftFlexibleArray : DiagGroup<"microsoft-flexible-array">;
 def MicrosoftExtraQualification : DiagGroup<"microsoft-extra-qualification">;
@@ -1710,10 +1711,11 @@ def Microsoft : DiagGroup<"microsoft",
      MicrosoftMutableReference, MicrosoftPureDefinition,
      MicrosoftUnionMemberReference, MicrosoftExplicitConstructorCall,
      MicrosoftEnumValue, MicrosoftDefaultArgRedefinition, MicrosoftTemplate,
-     MicrosoftRedeclareStatic, MicrosoftEnumForwardReference, MicrosoftGoto,
-     MicrosoftFlexibleArray, MicrosoftExtraQualification, MicrosoftCast,
-     MicrosoftConstInit, MicrosoftVoidPseudoDtor, MicrosoftAnonTag,
-     MicrosoftCommentPaste, MicrosoftEndOfFile,
+     MicrosoftRedeclareStatic, MicrosoftEnumForwardReference,
+     MicrosoftEnumTypedef, MicrosoftGoto, MicrosoftFlexibleArray,
+     MicrosoftExtraQualification, MicrosoftCast, MicrosoftConstInit,
+     MicrosoftVoidPseudoDtor, MicrosoftAnonTag, MicrosoftCommentPaste,
+     MicrosoftEndOfFile,
      MicrosoftInitFromPredefined, MicrosoftStringLiteralFromPredefined,
      MicrosoftInconsistentDllImport, MicrosoftInlineOnNonFunction,
      MicrosoftEmptyEnum]>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d293a9798da6a..c4022ce18393f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6700,6 +6700,10 @@ def err_forward_ref_enum : Error<
 def ext_ms_forward_ref_enum : ExtWarn<
   "forward references to 'enum' types are a Microsoft extension">,
   InGroup<MicrosoftEnumForwardReference>;
+def ext_ms_enum_typedef : ExtWarn<
+  "using an 'enum' specifier with a %select{typedef|type alias}0 name is a "
+  "Microsoft extension">,
+  InGroup<MicrosoftEnumTypedef>;
 def ext_forward_ref_enum_def : Extension<
   "redeclaration of already-defined enum %0 is a GNU extension">,
   InGroup<GNURedeclaredEnum>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3edf6f6c29500..52fb70c8d4eea 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1608,6 +1608,8 @@ class Sema final : public SemaBase {
   /// The parser maintains this state here.
   Scope *CurScope;
 
+  bool InFunctionParameterTypeInstantiation = false;
+
   mutable IdentifierInfo *Ident_super;
 
   std::unique_ptr<SemaAMDGPU> AMDGPUPtr;
@@ -4397,17 +4399,24 @@ class Sema final : public SemaBase {
   ///
   /// \param SkipBody If non-null, will be set to indicate if the caller should
   /// skip the definition of this tag and treat it as if it were a declaration.
-  DeclResult ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
-                      SourceLocation KWLoc, CXXScopeSpec &SS,
-                      IdentifierInfo *Name, SourceLocation NameLoc,
-                      const ParsedAttributesView &Attr, AccessSpecifier AS,
-                      SourceLocation ModulePrivateLoc,
-                      MultiTemplateParamsArg TemplateParameterLists,
-                      bool &OwnedDecl, bool &IsDependent,
-                      SourceLocation ScopedEnumKWLoc,
-                      bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
-                      bool IsTypeSpecifier, bool IsTemplateParamOrArg,
-                      OffsetOfKind OOK, SkipBodyInfo *SkipBody = nullptr);
+  ///
+  /// \param MSVCEnumType If non-null and the specifier names an enum typedef
+  /// accepted in MSVC compatibility mode, will be set to that type. In that
+  /// case, this function succeeds with a null declaration.
+  ///
+  /// \param IsFriend Whether a friend specifier has already been parsed in the
+  /// declaration containing this tag.
+  DeclResult
+  ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
+           CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
+           const ParsedAttributesView &Attr, AccessSpecifier AS,
+           SourceLocation ModulePrivateLoc,
+           MultiTemplateParamsArg TemplateParameterLists, bool &OwnedDecl,
+           bool &IsDependent, SourceLocation ScopedEnumKWLoc,
+           bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
+           bool IsTypeSpecifier, bool IsTemplateParamOrArg, OffsetOfKind OOK,
+           SkipBodyInfo *SkipBody = nullptr, TypeResult *MSVCEnumType = 
nullptr,
+           bool IsFriend = false);
 
   /// ActOnField - Each field of a C struct/union is passed into this in order
   /// to create a FieldDecl object for it.
@@ -12443,6 +12452,20 @@ class Sema final : public SemaBase {
                              const IdentifierInfo &II, SourceLocation IILoc,
                              bool DeducedTSTContext = true);
 
+  /// Builds and diagnoses an MSVC-compatible elaborated enum typedef type.
+  ///
+  /// Unqualified scope restrictions are enforced by the caller.
+  ///
+  /// \returns A null type, without diagnosing, if this is not MSVC-compatible
+  /// C++, if this occurs in a function prototype, if \p Keyword is not \c 
enum,
+  /// if \p Found does not name an enum typedef, or if the typedef and its
+  /// underlying tag share a name in the same redeclaration context.
+  QualType BuildMSVCEnumTypedefType(NamedDecl *Found,
+                                    ElaboratedTypeKeyword Keyword,
+                                    NestedNameSpecifier Qualifier,
+                                    SourceLocation NameLoc,
+                                    bool InFunctionPrototype = false);
+
   /// Rebuilds a type within the context of the current instantiation.
   ///
   /// The type \p T is part of the type of an out-of-line member definition of
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index fbecae7b80fb9..2539ff97fd4e1 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5398,17 +5398,20 @@ void Parser::ParseEnumSpecifier(SourceLocation 
StartLoc, DeclSpec &DS,
 
   bool Owned = false;
   bool IsDependent = false;
+  TypeResult MSVCEnumType;
   const char *PrevSpec = nullptr;
   unsigned DiagID;
   Decl *TagDecl =
-      Actions.ActOnTag(getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS,
-                    Name, NameLoc, attrs, AS, DS.getModulePrivateSpecLoc(),
-                    TParams, Owned, IsDependent, ScopedEnumKWLoc,
-                    IsScopedUsingClassTag,
+      Actions
+          .ActOnTag(getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS, Name,
+                    NameLoc, attrs, AS, DS.getModulePrivateSpecLoc(), TParams,
+                    Owned, IsDependent, ScopedEnumKWLoc, IsScopedUsingClassTag,
                     BaseType, DSC == DeclSpecContext::DSC_type_specifier,
                     DSC == DeclSpecContext::DSC_template_param ||
                         DSC == DeclSpecContext::DSC_template_type_arg,
-                    OffsetOfState, &SkipBody).get();
+                    OffsetOfState, &SkipBody, &MSVCEnumType,
+                    DS.isFriendSpecified())
+          .get();
 
   if (SkipBody.ShouldSkip) {
     assert(TUK == TagUseKind::Definition && "can only skip a definition");
@@ -5450,6 +5453,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, 
DeclSpec &DS,
     return;
   }
 
+  if (MSVCEnumType.isUsable()) {
+    if (DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc,
+                           NameLoc.isValid() ? NameLoc : StartLoc, PrevSpec,
+                           DiagID, MSVCEnumType.get(),
+                           Actions.getASTContext().getPrintingPolicy()))
+      Diag(StartLoc, DiagID) << PrevSpec;
+    return;
+  }
+
   if (!TagDecl) {
     // The action failed to produce an enumeration tag. If this is a
     // definition, consume the entire definition.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index db5e66cb96c3e..1c7843060e048 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18170,6 +18170,30 @@ static bool isAcceptableTagRedeclContext(Sema &S, 
DeclContext *OldDC,
   return false;
 }
 
+QualType Sema::BuildMSVCEnumTypedefType(NamedDecl *Found,
+                                        ElaboratedTypeKeyword Keyword,
+                                        NestedNameSpecifier Qualifier,
+                                        SourceLocation NameLoc,
+                                        bool InFunctionPrototype) {
+  if (Qualifier)
+    Found = Found->getUnderlyingDecl();
+  auto *TD = dyn_cast<TypedefNameDecl>(Found);
+  if (!getLangOpts().CPlusPlus || !getLangOpts().MSVCCompat ||
+      InFunctionPrototype || InFunctionParameterTypeInstantiation || !TD ||
+      Keyword != ElaboratedTypeKeyword::Enum ||
+      !TD->getUnderlyingType()->isEnumeralType())
+    return QualType();
+  if (TagDecl *Tag = TD->getUnderlyingType()->getAsTagDecl())
+    if (Tag->getDeclName() == TD->getDeclName() &&
+        Tag->getDeclContext()->getRedeclContext()->Equals(
+            TD->getDeclContext()->getRedeclContext()))
+      return QualType();
+
+  checkTypeDeclType(nullptr, DiagCtorKind::None, TD, NameLoc);
+  Diag(NameLoc, diag::ext_ms_enum_typedef) << isa<TypeAliasDecl>(TD);
+  return Context.getTypedefType(Keyword, Qualifier, TD);
+}
+
 DeclResult
 Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation 
KWLoc,
                CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
@@ -18179,7 +18203,8 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
                bool &IsDependent, SourceLocation ScopedEnumKWLoc,
                bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
                bool IsTypeSpecifier, bool IsTemplateParamOrArg,
-               OffsetOfKind OOK, SkipBodyInfo *SkipBody) {
+               OffsetOfKind OOK, SkipBodyInfo *SkipBody,
+               TypeResult *MSVCEnumType, bool IsFriend) {
   // If this is not a definition, it must have a name.
   IdentifierInfo *OrigName = Name;
   assert((Name != nullptr || TUK == TagUseKind::Definition) &&
@@ -18190,6 +18215,42 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
   TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
   bool ScopedEnum = ScopedEnumKWLoc.isValid();
 
+  auto SetMSVCEnumType = [&](NamedDecl *Found) {
+    NamedDecl *TypeDecl = Found;
+    if (!SS.isEmpty() ||
+        (isa<UsingShadowDecl>(Found) && Found->getDeclContext()->isRecord()))
+      TypeDecl = Found->getUnderlyingDecl();
+    auto *TD = dyn_cast<TypedefNameDecl>(TypeDecl);
+    if (!MSVCEnumType || !TD || Kind != TagTypeKind::Enum ||
+        TUK != TagUseKind::Reference || (S && S->containedInPrototypeScope()))
+      return false;
+    // MSVC accepts an unqualified typedef only in class scope and not after a
+    // friend specifier.
+    if (SS.isEmpty()) {
+      if (IsFriend)
+        return false;
+      auto *CurRecord = dyn_cast<CXXRecordDecl>(CurContext);
+      if (!CurRecord)
+        return false;
+      DeclContext *TDContext = TD->getDeclContext();
+      auto *TDRecord = dyn_cast<CXXRecordDecl>(TDContext);
+      if (!TDContext->Encloses(CurContext) &&
+          !(TDRecord && CurRecord->isDerivedFrom(TDRecord)))
+        return false;
+    }
+
+    NestedNameSpecifier Qualifier = SS.getScopeRep();
+    NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
+    QualType T = BuildMSVCEnumTypedefType(TypeDecl, 
ElaboratedTypeKeyword::Enum,
+                                          Qualifier, NameLoc);
+    if (T.isNull())
+      return false;
+    TypeLocBuilder TLB;
+    TLB.push<TypedefTypeLoc>(T).set(KWLoc, QualifierLoc, NameLoc);
+    *MSVCEnumType = CreateParsedType(T, TLB.getTypeSourceInfo(Context, T));
+    return true;
+  };
+
   // FIXME: Check member specializations more carefully.
   bool isMemberSpecialization = false;
   bool IsInjectedClassName = false;
@@ -18658,6 +18719,8 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
     // okay according to the likely resolution of an open issue;
     // see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#407
     if (getLangOpts().CPlusPlus) {
+      if (SetMSVCEnumType(DirectPrevDecl))
+        return (Decl *)nullptr;
       if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(PrevDecl)) {
         if (TagDecl *Tag = TD->getUnderlyingType()->getAsTagDecl()) {
           if (Tag->getDeclName() == Name &&
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 06ea12ca7bd73..2adf5e7c34697 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3173,6 +3173,8 @@ Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
                        bool ExpectParameterPack, bool EvaluateConstraint) {
   TypeSourceInfo *OldTSI = OldParm->getTypeSourceInfo();
   TypeSourceInfo *NewTSI = nullptr;
+  llvm::SaveAndRestore InParameterType(InFunctionParameterTypeInstantiation,
+                                       true);
 
   TypeLoc OldTL = OldTSI->getTypeLoc();
   if (PackExpansionTypeLoc ExpansionTL = OldTL.getAs<PackExpansionTypeLoc>()) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index d362dd7bf999c..9a543fb5e497c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -49,6 +49,7 @@
 #include "clang/Sema/Template.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include <algorithm>
 #include <optional>
 
@@ -148,6 +149,7 @@ class TreeTransform {
 
 protected:
   Sema &SemaRef;
+  bool TransformingFunctionParameterType = false;
 
   /// The set of local declarations that have been transformed, for
   /// cases where we are forced to build new declarations within the 
transformer
@@ -173,6 +175,10 @@ class TreeTransform {
   /// this tree transform.
   Sema &getSema() const { return SemaRef; }
 
+  bool isTransformingFunctionParameterType() const {
+    return TransformingFunctionParameterType;
+  }
+
   /// Whether the transformation should always rebuild AST nodes, even
   /// if none of the children have changed.
   ///
@@ -1251,6 +1257,11 @@ class TreeTransform {
       case LookupResultKind::FoundOverloaded:
       case LookupResultKind::FoundUnresolvedValue: {
         NamedDecl *SomeDecl = Result.getRepresentativeDecl();
+        if (QualType T = SemaRef.BuildMSVCEnumTypedefType(
+                SomeDecl, Keyword, QualifierLoc.getNestedNameSpecifier(), 
IdLoc,
+                getDerived().isTransformingFunctionParameterType());
+            !T.isNull())
+          return T;
         NonTagKind NTK = SemaRef.getNonTagTypeDeclKind(SomeDecl, Kind);
         SemaRef.Diag(IdLoc, diag::err_tag_reference_non_tag)
             << SomeDecl << NTK << Kind;
@@ -6519,6 +6530,8 @@ bool TreeTransform<Derived>::TransformFunctionTypeParams(
     SmallVectorImpl<ParmVarDecl *> *PVars,
     Sema::ExtParameterInfoBuilder &PInfos,
     unsigned *LastParamTransformed) {
+  llvm::SaveAndRestore TransformingParameter(TransformingFunctionParameterType,
+                                             true);
   int indexAdjustment = 0;
 
   unsigned NumParams = Params.size();
diff --git a/clang/test/AST/ms-enum-typedef.cpp 
b/clang/test/AST/ms-enum-typedef.cpp
new file mode 100644
index 0000000000000..9d916c0329263
--- /dev/null
+++ b/clang/test/AST/ms-enum-typedef.cpp
@@ -0,0 +1,39 @@
+// Test without serialization:
+// RUN: %clang_cc1 -std=c++11 -fms-compatibility \
+// RUN:   -Wno-microsoft-enum-typedef -ast-dump -ast-dump-filter Use %s \
+// RUN:   | FileCheck %s
+//
+// Test with serialization:
+// RUN: %clang_cc1 -std=c++11 -fms-compatibility \
+// RUN:   -Wno-microsoft-enum-typedef -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -std=c++11 -fms-compatibility \
+// RUN:   -Wno-microsoft-enum-typedef -include-pch %t -ast-dump-all \
+// RUN:   -ast-dump-filter Use /dev/null \
+// RUN:   | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
+// RUN:   | FileCheck %s
+
+typedef enum Underlying { Value } Alias;
+
+struct Use {
+  enum Alias *member;
+};
+
+namespace Qualified {
+typedef enum Underlying { Value } Alias;
+}
+
+struct QualifiedUse {
+  enum Qualified::Alias *member;
+};
+
+template <class T>
+struct DependentUse {
+  enum T::Alias *member;
+};
+
+// CHECK: CXXRecordDecl {{.*}} struct Use definition
+// CHECK: FieldDecl {{.*}} member 'enum Alias *'
+// CHECK: CXXRecordDecl {{.*}} struct QualifiedUse definition
+// CHECK: FieldDecl {{.*}} member 'enum Qualified::Alias *'
+// CHECK: CXXRecordDecl {{.*}} struct DependentUse definition
+// CHECK: FieldDecl {{.*}} member 'enum T::Alias *'
diff --git a/clang/test/Sema/ms-enum-typedef.c 
b/clang/test/Sema/ms-enum-typedef.c
new file mode 100644
index 0000000000000..6ffca2f3c22ae
--- /dev/null
+++ b/clang/test/Sema/ms-enum-typedef.c
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -fms-compatibility -Wno-microsoft-enum-forward-reference 
-fsyntax-only -verify %s
+// expected-no-diagnostics
+
+typedef enum Original { Value } Alias;
+enum Alias *tag_pointer;
+Alias *typedef_pointer;
+
+_Static_assert(!__builtin_types_compatible_p(enum Alias, Alias),
+               "the enum tag and typedef must remain distinct in C");
diff --git a/clang/test/SemaCXX/ms-enum-typedef-warning.cpp 
b/clang/test/SemaCXX/ms-enum-typedef-warning.cpp
new file mode 100644
index 0000000000000..a6c76df2907a2
--- /dev/null
+++ b/clang/test/SemaCXX/ms-enum-typedef-warning.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fms-compatibility \
+// RUN:   -Werror=microsoft-enum-typedef -verify=werror %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fms-compatibility \
+// RUN:   -Wno-microsoft-enum-typedef -verify=nowarn %s
+
+// nowarn-no-diagnostics
+
+typedef enum Underlying { Value } Alias;
+
+struct Use {
+  enum Alias *member; // werror-error {{using an 'enum' specifier with a 
typedef name is a Microsoft extension}}
+};
diff --git a/clang/test/SemaCXX/ms-enum-typedef.cpp 
b/clang/test/SemaCXX/ms-enum-typedef.cpp
new file mode 100644
index 0000000000000..3d81da139dc2b
--- /dev/null
+++ b/clang/test/SemaCXX/ms-enum-typedef.cpp
@@ -0,0 +1,301 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,nomsvc %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,msvc 
-fms-compatibility -DMSVC_COMPAT %s
+
+typedef enum NamedEnum { NamedValue } NamedAlias; // msvc-note 4 {{declared 
here}}
+typedef enum { AnonymousValue } AnonymousAlias;
+typedef enum SameNameEnum { SameNameValue } SameNameEnum;
+
+struct StandardCase {
+  enum SameNameEnum *member;
+};
+
+namespace StandardUsing {
+typedef enum SameName { Value } SameName;
+}
+using StandardUsing::SameName;
+
+struct StandardUsingCase {
+  enum SameName *member;
+};
+
+typedef enum FriendNamed { FriendValue } FriendAlias; // expected-note 
{{declared here}}
+typedef enum AfterFriendNamed { AfterFriendValue } AfterFriendAlias; // 
nomsvc-note {{declared here}}
+typedef enum TemplateFriendNamed { TemplateFriendValue } TemplateFriendAlias; 
// expected-note {{declared here}}
+typedef enum AfterTemplateFriendNamed { AfterTemplateFriendValue } 
AfterTemplateFriendAlias; // nomsvc-note {{declared here}}
+typedef enum FriendReturnNamed { FriendReturnValue } FriendReturnAlias; // 
expected-note {{declared here}}
+typedef enum TemplateFriendReturnNamed { TemplateFriendReturnValue } 
TemplateFriendReturnAlias; // expected-note {{declared here}}
+typedef enum LateFriendNamed { LateFriendValue } LateFriendAlias; // 
nomsvc-note {{declared here}}
+typedef enum NestedFriendNamed { NestedFriendValue } NestedFriendAlias; // 
expected-note {{declared here}}
+typedef enum NestedInnerNamed { NestedInnerValue } NestedInnerAlias; // 
nomsvc-note {{declared here}}
+typedef enum NestedOuterNamed { NestedOuterValue } NestedOuterAlias; // 
nomsvc-note {{declared here}}
+
+struct FriendUse {
+  friend void friend_alias(enum FriendAlias *); // expected-error {{typedef 
'FriendAlias' cannot be referenced with the 'enum' specifier}}
+  enum AfterFriendAlias *member; // nomsvc-error {{typedef 'AfterFriendAlias' 
cannot be referenced with the 'enum' specifier}}
+                                 // msvc-warning@-1 {{using an 'enum' 
specifier with a typedef name is a Microsoft extension}}
+};
+
+struct TemplateFriendUse {
+  template <class T>
+  friend void template_friend(enum TemplateFriendAlias *); // expected-error 
{{typedef 'TemplateFriendAlias' cannot be referenced with the 'enum' specifier}}
+  enum AfterTemplateFriendAlias *member; // nomsvc-error {{typedef 
'AfterTemplateFriendAlias' cannot be referenced with the 'enum' specifier}}
+                                         // msvc-warning@-1 {{using an 'enum' 
specifier with a typedef name is a Microsoft extension}}
+};
+
+struct FriendReturnUse {
+  friend enum FriendReturnAlias *friend_return(); // expected-error {{typedef 
'FriendReturnAlias' cannot be referenced with the 'enum' specifier}}
+};
+
+struct TemplateFriendReturnUse {
+  template <class T>
+  friend enum TemplateFriendReturnAlias *template_friend_return(); // 
expected-error {{typedef 'TemplateFriendReturnAlias' cannot be referenced with 
the 'enum' specifier}}
+};
+
+struct LateFriendUse {
+  enum LateFriendAlias friend *late_friend(); // nomsvc-error {{typedef 
'LateFriendAlias' cannot be referenced with the 'enum' specifier}}
+                                              // msvc-warning@-1 {{using an 
'enum' specifier with a typedef name is a Microsoft extension}}
+};
+
+struct NestedFriendUse {
+  struct Inner {
+    friend void nested_friend(enum NestedFriendAlias *); // expected-error 
{{typedef 'NestedFriendAlias' cannot be referenced with the 'enum' specifier}}
+    enum NestedInnerAlias *member; // nomsvc-error {{typedef 
'NestedInnerAlias' cannot be referenced with the 'enum' specifier}}
+                                   // msvc-warning@-1 {{using an 'enum' 
specifier with a typedef name is a Microsoft extension}}
+  };
+  enum NestedOuterAlias *member; // nomsvc-error {{typedef 'NestedOuterAlias' 
cannot be referenced with the 'enum' specifier}}
+                                 // msvc-warning@-1 {{using an 'enum' 
specifier with a typedef name is a Microsoft extension}}
+};
+
+#ifdef MSVC_COMPAT
+struct FunctionParameterUse {
+  void unqualified(enum NamedAlias *); // msvc-error {{typedef 'NamedAlias' 
cannot be referenced with the 'enum' specifier}}
+  void qualified(enum ::NamedAlias *); // msvc-error {{typedef 'NamedAlias' 
cannot be referenced with the 'enum' specifier}}
+  void nested(void (*)(enum ::NamedAlias *)); // msvc-error {{typedef 
'NamedAlias' cannot be referenced with the 'enum' specifier}}
+};
+
+struct Holder {
+  enum AnonymousAlias *member; // msvc-warning {{using an 'enum' specifier 
with a typedef name is a Microsoft extension}}
+};
+
+static_assert(__is_same(decltype(Holder::member), AnonymousAlias *), "");
+
+struct OwnedAlias {
+  typedef enum Named { Value } Alias; // nomsvc-note {{declared here}}
+  enum Alias *member; // nomsvc-error {{typedef 'Alias' cannot be referenced 
with the 'enum' specifier}}
+                      // msvc-warning@-1 {{using an 'enum' specifier with a 
typedef name is a Microsoft extension}}
+};
+
+static_assert(__is_same(decltype(OwnedAlias::member), OwnedAlias::Alias *), 
"");
+
+struct AliasBase {
+  typedef enum Named { Value } Alias; // nomsvc-note {{declared here}}
+};
+
+struct InheritedAlias : AliasBase {
+  enum Alias *member; // nomsvc-error {{typedef 'Alias' cannot be referenced 
with the 'enum' specifier}}
+                      // msvc-warning@-1 {{using an 'enum' specifier with a 
typedef name is a Microsoft extension}}
+};
+
+static_assert(__is_same(decltype(InheritedAlias::member), AliasBase::Alias *),
+              "");
+
+typedef NamedEnum ChainedAlias; // nomsvc-note {{declared here}}
+
+struct ChainedUse {
+  enum ChainedAlias *member; // nomsvc-error {{typedef 'ChainedAlias' cannot 
be referenced with the 'enum' specifier}}
+                             // msvc-warning@-1 {{using an 'enum' specifier 
with a typedef name is a Microsoft extension}}
+};
+
+enum class ScopedNamed { ScopedValue };
+typedef ScopedNamed ScopedAlias; // nomsvc-note {{declared here}}
+
+struct ScopedUse {
+  enum ScopedAlias *member; // nomsvc-error {{typedef 'ScopedAlias' cannot be 
referenced with the 'enum' specifier}}
+                            // msvc-warning@-1 {{using an 'enum' specifier 
with a typedef name is a Microsoft extension}}
+};
+
+struct BodyUse {
+  void f() {
+    enum NamedAlias *value; // expected-error {{typedef 'NamedAlias' cannot be 
referenced with the 'enum' specifier}}
+    (void)value;
+  }
+};
+#endif
+
+typedef const NamedEnum ConstAlias; // nomsvc-note {{declared here}}
+
+void qualified() {
+  enum ::ConstAlias value = NamedValue; // nomsvc-error {{typedef 'ConstAlias' 
cannot be referenced with the 'enum' specifier}}
+                                        // msvc-warning@-1 {{using an 'enum' 
specifier with a typedef name is a Microsoft extension}}
+                                        // msvc-note@-2 {{variable 'value' 
declared const here}}
+#ifdef MSVC_COMPAT
+  static_assert(__is_same(decltype(value), ConstAlias), "");
+#endif
+  value = NamedValue; // msvc-error {{cannot assign to variable 'value' with 
const-qualified type 'enum ::ConstAlias'}}
+}
+
+using UsingAlias = NamedEnum; // nomsvc-note {{declared here}}
+
+void type_alias() {
+  enum ::UsingAlias value; // nomsvc-error {{type alias 'UsingAlias' cannot be 
referenced with the 'enum' specifier}}
+                           // msvc-warning@-1 {{using an 'enum' specifier with 
a type alias name is a Microsoft extension}}
+  (void)value;
+}
+
+namespace Qualified {
+typedef enum Named { Value } Alias; // nomsvc-note 2 {{declared here}}
+}
+
+enum Qualified::Alias *qualified_global; // nomsvc-error {{typedef 'Alias' 
cannot be referenced with the 'enum' specifier}}
+                                         // msvc-warning@-1 {{using an 'enum' 
specifier with a typedef name is a Microsoft extension}}
+
+namespace QualifiedImport {
+using Qualified::Alias;
+}
+
+enum QualifiedImport::Alias *qualified_import; // nomsvc-error {{typedef 
'Alias' cannot be referenced with the 'enum' specifier}}
+                                               // msvc-warning@-1 {{using an 
'enum' specifier with a typedef name is a Microsoft extension}}
+
+#ifdef MSVC_COMPAT
+static_assert(__is_same(decltype(qualified_import), Qualified::Alias *), "");
+#endif
+
+namespace Imported {
+typedef enum Named { Value } Alias; // expected-note 2 {{declared here}}
+}
+using Imported::Alias;
+
+void imported_alias() {
+  enum Alias *value; // expected-error {{typedef 'Alias' cannot be referenced 
with the 'enum' specifier}}
+  (void)value;
+}
+
+enum Alias *file_using_alias; // expected-error {{typedef 'Alias' cannot be 
referenced with the 'enum' specifier}}
+
+struct ClassUsingBase {
+  typedef enum Underlying { Value } Alias; // nomsvc-note {{declared here}}
+};
+
+struct ClassUsingDerived : ClassUsingBase {
+  using ClassUsingBase::Alias;
+  enum Alias *member; // nomsvc-error {{typedef 'Alias' cannot be referenced 
with the 'enum' specifier}}
+                      // msvc-warning@-1 {{using an 'enum' specifier with a 
typedef name is a Microsoft extension}}
+};
+
+namespace Directive {
+typedef enum Named { Value } DirectiveAlias; // expected-note {{declared here}}
+}
+using namespace Directive;
+
+struct DirectiveUse {
+  enum DirectiveAlias *value; // expected-error {{typedef 'DirectiveAlias' 
cannot be referenced with the 'enum' specifier}}
+};
+
+namespace FileScope {
+typedef enum Named { Value } Alias; // expected-note {{declared here}}
+enum Alias *value; // expected-error {{typedef 'Alias' cannot be referenced 
with the 'enum' specifier}}
+}
+
+typedef enum LocalNamed { LocalValue } LocalAlias; // expected-note 2 
{{declared here}}
+
+void free_unqualified(enum LocalAlias *); // expected-error {{typedef 
'LocalAlias' cannot be referenced with the 'enum' specifier}}
+
+void direct_local_alias() {
+  enum LocalAlias *value; // expected-error {{typedef 'LocalAlias' cannot be 
referenced with the 'enum' specifier}}
+  (void)value;
+}
+
+typedef int IntAlias; // expected-note {{declared here}}
+
+void not_an_enum() {
+  enum ::IntAlias value; // expected-error {{typedef 'IntAlias' cannot be 
referenced with the 'enum' specifier}}
+  (void)value;
+}
+
+#ifdef MSVC_COMPAT
+typedef enum DeprecatedEnum { DeprecatedValue } DeprecatedAlias
+    __attribute__((deprecated("old enum alias"))); // msvc-note 
{{'DeprecatedAlias' has been explicitly marked deprecated here}}
+
+struct DeprecatedUse {
+  enum DeprecatedAlias *value; // msvc-warning {{'DeprecatedAlias' is 
deprecated: old enum alias}}
+                               // msvc-warning@-1 {{using an 'enum' specifier 
with a typedef name is a Microsoft extension}}
+};
+
+struct DeprecatedUsingBase {
+  typedef enum Underlying { Value } Alias
+      __attribute__((deprecated("old class enum alias"))); // msvc-note 
{{'Alias' has been explicitly marked deprecated here}}
+};
+
+struct DeprecatedUsingDerived : DeprecatedUsingBase {
+  using DeprecatedUsingBase::Alias;
+  enum Alias *value; // msvc-warning {{'Alias' is deprecated: old class enum 
alias}}
+                     // msvc-warning@-1 {{using an 'enum' specifier with a 
typedef name is a Microsoft extension}}
+};
+
+namespace Unavailable {
+typedef enum Enum { Value } Alias // msvc-note {{'Alias' has been explicitly 
marked unavailable here}}
+    __attribute__((unavailable("unavailable enum alias")));
+}
+
+struct UnavailableUse {
+  enum Unavailable::Alias *value; // msvc-error {{'Alias' is unavailable: 
unavailable enum alias}}
+                                 // msvc-warning@-1 {{using an 'enum' 
specifier with a typedef name is a Microsoft extension}}
+};
+
+struct PrivateOwner {
+private:
+  typedef enum Named { Value } Alias; // msvc-note 2 {{declared private here}}
+};
+
+enum PrivateOwner::Alias *private_qualified; // msvc-error {{'Alias' is a 
private member of 'PrivateOwner'}}
+                                             // msvc-warning@-1 {{using an 
'enum' specifier with a typedef name is a Microsoft extension}}
+
+struct PrivateDerived : PrivateOwner {
+  enum Alias *private_inherited; // msvc-error {{'Alias' is a private member 
of 'PrivateOwner'}}
+                                 // msvc-warning@-1 {{using an 'enum' 
specifier with a typedef name is a Microsoft extension}}
+};
+
+struct PrivateUsingBase {
+private:
+  typedef enum Underlying { Value } Alias; // msvc-note {{declared private 
here}}
+};
+
+struct PrivateUsingDerived : PrivateUsingBase {
+  using PrivateUsingBase::Alias; // msvc-error {{'Alias' is a private member 
of 'PrivateUsingBase'}}
+  enum Alias *private_using; // msvc-warning {{using an 'enum' specifier with 
a typedef name is a Microsoft extension}}
+};
+
+template <class T>
+struct DependentUse {
+  enum T::Alias *value; // msvc-warning {{using an 'enum' specifier with a 
typedef name is a Microsoft extension}}
+};
+
+struct DependentHost {
+  typedef enum Named { Value } Alias; // msvc-note 2 {{declared here}}
+};
+
+template struct DependentUse<DependentHost>; // msvc-note {{in instantiation 
of template class 'DependentUse<DependentHost>' requested here}}
+
+template <class T>
+struct DependentParameterUse {
+  void f(enum T::Alias *); // msvc-error {{typedef 'Alias' cannot be 
referenced with the 'enum' specifier}}
+};
+
+template struct DependentParameterUse<DependentHost>; // msvc-note {{in 
instantiation of template class 'DependentParameterUse<DependentHost>' 
requested here}}
+
+template <class T>
+using DependentFunctionType = void(enum T::Alias *); // msvc-error {{typedef 
'Alias' cannot be referenced with the 'enum' specifier}}
+
+using InstantiatedFunctionType = DependentFunctionType<DependentHost>; // 
msvc-note {{in instantiation of template type alias 'DependentFunctionType' 
requested here}}
+
+struct DependentSameNameHost {
+  typedef enum SameName { Value } SameName;
+};
+
+template <class T>
+struct DependentSameNameUse {
+  enum T::SameName *value;
+};
+
+template struct DependentSameNameUse<DependentSameNameHost>;
+#endif

>From 575c8cd6485cc8a97980daeb78061e8a7b21af86 Mon Sep 17 00:00:00 2001
From: Zachary Henkel <[email protected]>
Date: Thu, 24 Sep 2026 08:24:43 -0700
Subject: [PATCH 2/7] [clang] Scope enum typedef parameter checks to type
 substitution

**AI-authored commit** - Analysis, code changes, tests, and wording were 
produced by an AI agent. zahen reviewed the change before commit.

Follow up on @ojhunt's review comment in #225913 questioning the parameter-type 
state added to SubstParmVarDecl.

Replace the Sema-wide function-parameter instantiation flag with state local to 
the active TreeTransform. This keeps the restriction active while rebuilding an 
actual parameter type without leaking it into nested default template 
arguments, constraints, or attributes.

Route both ordinary parameter substitution and parameter-pack pattern 
substitution through the scoped transformation path. Add regressions covering a 
nested default template argument and a dependent parameter pack.

Co-authored-by: Copilot <[email protected]>
Copilot-Session: f1668d1f-2a77-45bf-87ce-93f4647a5386
---
 clang/include/clang/Sema/Sema.h            | 12 +++++-
 clang/lib/Sema/SemaDecl.cpp                |  3 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 47 +++++++++++++++++++---
 clang/lib/Sema/TreeTransform.h             | 12 ++++++
 clang/test/SemaCXX/ms-enum-typedef.cpp     | 27 +++++++++++++
 5 files changed, 91 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 52fb70c8d4eea..5f4a6aacd9ee6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1608,8 +1608,6 @@ class Sema final : public SemaBase {
   /// The parser maintains this state here.
   Scope *CurScope;
 
-  bool InFunctionParameterTypeInstantiation = false;
-
   mutable IdentifierInfo *Ident_super;
 
   std::unique_ptr<SemaAMDGPU> AMDGPUPtr;
@@ -13916,6 +13914,16 @@ class Sema final : public SemaBase {
                             const MultiLevelTemplateArgumentList &TemplateArgs,
                             SourceLocation Loc, DeclarationName Entity);
 
+  TypeSourceInfo *
+  SubstFunctionParameterType(TypeSourceInfo *T,
+                             const MultiLevelTemplateArgumentList 
&TemplateArgs,
+                             SourceLocation Loc, DeclarationName Entity);
+
+  TypeSourceInfo *
+  SubstFunctionParameterType(TypeLoc TL,
+                             const MultiLevelTemplateArgumentList 
&TemplateArgs,
+                             SourceLocation Loc, DeclarationName Entity);
+
   TypeSourceInfo *
   SubstFriendType(TypeSourceInfo *TSI,
                   const MultiLevelTemplateArgumentList &TemplateArgs,
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 1c7843060e048..47e8106fd9de8 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18179,8 +18179,7 @@ QualType Sema::BuildMSVCEnumTypedefType(NamedDecl 
*Found,
     Found = Found->getUnderlyingDecl();
   auto *TD = dyn_cast<TypedefNameDecl>(Found);
   if (!getLangOpts().CPlusPlus || !getLangOpts().MSVCCompat ||
-      InFunctionPrototype || InFunctionParameterTypeInstantiation || !TD ||
-      Keyword != ElaboratedTypeKeyword::Enum ||
+      InFunctionPrototype || !TD || Keyword != ElaboratedTypeKeyword::Enum ||
       !TD->getUnderlyingType()->isEnumeralType())
     return QualType();
   if (TagDecl *Tag = TD->getUnderlyingType()->getAsTagDecl())
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 2adf5e7c34697..0aabe9f2533c2 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2937,6 +2937,42 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL,
   return TLB.getTypeSourceInfo(Context, Result);
 }
 
+TypeSourceInfo *
+Sema::SubstFunctionParameterType(TypeSourceInfo *T,
+                                 const MultiLevelTemplateArgumentList &Args,
+                                 SourceLocation Loc, DeclarationName Entity) {
+  if (!T->getType()->isInstantiationDependentType() &&
+      !T->getType()->isVariablyModifiedType())
+    return T;
+
+  TemplateInstantiator Instantiator(*this, Args, Loc, Entity);
+  return Instantiator.TransformFunctionParameterType(T);
+}
+
+TypeSourceInfo *
+Sema::SubstFunctionParameterType(TypeLoc TL,
+                                 const MultiLevelTemplateArgumentList &Args,
+                                 SourceLocation Loc, DeclarationName Entity) {
+  if (TL.getType().isNull())
+    return nullptr;
+
+  if (!TL.getType()->isInstantiationDependentType() &&
+      !TL.getType()->isVariablyModifiedType()) {
+    TypeLocBuilder TLB;
+    TLB.pushFullCopy(TL);
+    return TLB.getTypeSourceInfo(Context, TL.getType());
+  }
+
+  TemplateInstantiator Instantiator(*this, Args, Loc, Entity);
+  TypeLocBuilder TLB;
+  TLB.reserve(TL.getFullDataSize());
+  QualType Result = Instantiator.TransformFunctionParameterType(TLB, TL);
+  if (Result.isNull())
+    return nullptr;
+
+  return TLB.getTypeSourceInfo(Context, Result);
+}
+
 /// Deprecated form of the above.
 QualType Sema::SubstType(QualType T,
                          const MultiLevelTemplateArgumentList &TemplateArgs,
@@ -3173,16 +3209,15 @@ Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
                        bool ExpectParameterPack, bool EvaluateConstraint) {
   TypeSourceInfo *OldTSI = OldParm->getTypeSourceInfo();
   TypeSourceInfo *NewTSI = nullptr;
-  llvm::SaveAndRestore InParameterType(InFunctionParameterTypeInstantiation,
-                                       true);
 
   TypeLoc OldTL = OldTSI->getTypeLoc();
   if (PackExpansionTypeLoc ExpansionTL = OldTL.getAs<PackExpansionTypeLoc>()) {
 
     // We have a function parameter pack. Substitute into the pattern of the
     // expansion.
-    NewTSI = SubstType(ExpansionTL.getPatternLoc(), TemplateArgs,
-                       OldParm->getLocation(), OldParm->getDeclName());
+    NewTSI = SubstFunctionParameterType(ExpansionTL.getPatternLoc(),
+                                        TemplateArgs, OldParm->getLocation(),
+                                        OldParm->getDeclName());
     if (!NewTSI)
       return nullptr;
 
@@ -3203,8 +3238,8 @@ Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
       return nullptr;
     }
   } else {
-    NewTSI = SubstType(OldTSI, TemplateArgs, OldParm->getLocation(),
-                       OldParm->getDeclName());
+    NewTSI = SubstFunctionParameterType(
+        OldTSI, TemplateArgs, OldParm->getLocation(), OldParm->getDeclName());
   }
 
   if (!NewTSI)
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 9a543fb5e497c..37864629c1524 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -403,6 +403,18 @@ class TreeTransform {
   TypeSourceInfo *TransformTypeWithDeducedTST(TypeSourceInfo *TSI);
   /// @}
 
+  TypeSourceInfo *TransformFunctionParameterType(TypeSourceInfo *TSI) {
+    llvm::SaveAndRestore TransformingParameter(
+        TransformingFunctionParameterType, true);
+    return getDerived().TransformType(TSI);
+  }
+
+  QualType TransformFunctionParameterType(TypeLocBuilder &TLB, TypeLoc TL) {
+    llvm::SaveAndRestore TransformingParameter(
+        TransformingFunctionParameterType, true);
+    return getDerived().TransformType(TLB, TL);
+  }
+
   /// The reason why the value of a statement is not discarded, if any.
   enum class StmtDiscardKind {
     Discarded,
diff --git a/clang/test/SemaCXX/ms-enum-typedef.cpp 
b/clang/test/SemaCXX/ms-enum-typedef.cpp
index 3d81da139dc2b..5e9c77782067a 100644
--- a/clang/test/SemaCXX/ms-enum-typedef.cpp
+++ b/clang/test/SemaCXX/ms-enum-typedef.cpp
@@ -283,6 +283,33 @@ struct DependentParameterUse {
 
 template struct DependentParameterUse<DependentHost>; // msvc-note {{in 
instantiation of template class 'DependentParameterUse<DependentHost>' 
requested here}}
 
+#ifdef MSVC_COMPAT
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmicrosoft-enum-typedef"
+template <class T, class U = enum T::Alias>
+struct DependentDefaultArgCarrier {
+  U value;
+};
+
+template <class T>
+void dependent_default_arg_param(DependentDefaultArgCarrier<T>) {}
+
+template void dependent_default_arg_param<DependentHost>(
+    DependentDefaultArgCarrier<DependentHost>);
+#pragma clang diagnostic pop
+
+struct DependentPackHost {
+  typedef enum Named { Value } Alias; // msvc-note {{declared here}}
+};
+
+template <class... Ts>
+struct DependentPackUse {
+  void f(enum Ts::Alias *...); // msvc-error {{typedef 'Alias' cannot be 
referenced with the 'enum' specifier}}
+};
+
+template struct DependentPackUse<DependentPackHost>; // msvc-note {{in 
instantiation of template class 'DependentPackUse<DependentPackHost>' requested 
here}}
+#endif
+
 template <class T>
 using DependentFunctionType = void(enum T::Alias *); // msvc-error {{typedef 
'Alias' cannot be referenced with the 'enum' specifier}}
 

>From 67acd583a10c0c9a2d46d094a1433f11654dd0eb Mon Sep 17 00:00:00 2001
From: Zachary Henkel <[email protected]>
Date: Thu, 24 Sep 2026 08:44:17 -0700
Subject: [PATCH 3/7] Fix formatting

**AI-authored commit** - Source lookup and code change were produced by an AI 
agent; reviewed by zahen.

Co-authored-by: Copilot <[email protected]>
---
 clang/lib/Parse/ParseDecl.cpp | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 2539ff97fd4e1..87ba92415557e 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5401,17 +5401,15 @@ void Parser::ParseEnumSpecifier(SourceLocation 
StartLoc, DeclSpec &DS,
   TypeResult MSVCEnumType;
   const char *PrevSpec = nullptr;
   unsigned DiagID;
-  Decl *TagDecl =
-      Actions
-          .ActOnTag(getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS, Name,
-                    NameLoc, attrs, AS, DS.getModulePrivateSpecLoc(), TParams,
-                    Owned, IsDependent, ScopedEnumKWLoc, IsScopedUsingClassTag,
-                    BaseType, DSC == DeclSpecContext::DSC_type_specifier,
-                    DSC == DeclSpecContext::DSC_template_param ||
-                        DSC == DeclSpecContext::DSC_template_type_arg,
-                    OffsetOfState, &SkipBody, &MSVCEnumType,
-                    DS.isFriendSpecified())
-          .get();
+  DeclResult TagResult = Actions.ActOnTag(
+      getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS, Name, NameLoc,
+      attrs, AS, DS.getModulePrivateSpecLoc(), TParams, Owned, IsDependent,
+      ScopedEnumKWLoc, IsScopedUsingClassTag, BaseType,
+      DSC == DeclSpecContext::DSC_type_specifier,
+      DSC == DeclSpecContext::DSC_template_param ||
+          DSC == DeclSpecContext::DSC_template_type_arg,
+      OffsetOfState, &SkipBody, &MSVCEnumType, DS.isFriendSpecified());
+  Decl *TagDecl = TagResult.get();
 
   if (SkipBody.ShouldSkip) {
     assert(TUK == TagUseKind::Definition && "can only skip a definition");

>From dac8b328e120774d0cd58b34b002661d37c70ee6 Mon Sep 17 00:00:00 2001
From: Zachary Henkel <[email protected]>
Date: Thu, 24 Sep 2026 09:06:32 -0700
Subject: [PATCH 4/7] [clang] Apply minor enum typedef review feedback

**AI-authored commit** - Source lookup, code changes, tests, and wording were 
produced by an AI agent; reviewed by zahen.

Apply minor review suggestions

Co-authored-by: Copilot <[email protected]>
Copilot-Session: f1668d1f-2a77-45bf-87ce-93f4647a5386
---
 clang/include/clang/Sema/Sema.h        | 10 +++++-----
 clang/lib/Sema/SemaDecl.cpp            | 17 +++++++++--------
 clang/lib/Sema/TreeTransform.h         |  2 +-
 clang/test/SemaCXX/ms-enum-typedef.cpp |  6 ++++--
 4 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5f4a6aacd9ee6..7deea2a4c5caf 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12458,11 +12458,11 @@ class Sema final : public SemaBase {
   /// C++, if this occurs in a function prototype, if \p Keyword is not \c 
enum,
   /// if \p Found does not name an enum typedef, or if the typedef and its
   /// underlying tag share a name in the same redeclaration context.
-  QualType BuildMSVCEnumTypedefType(NamedDecl *Found,
-                                    ElaboratedTypeKeyword Keyword,
-                                    NestedNameSpecifier Qualifier,
-                                    SourceLocation NameLoc,
-                                    bool InFunctionPrototype = false);
+  QualType TryBuildMSVCEnumTypedefType(NamedDecl *Found,
+                                       ElaboratedTypeKeyword Keyword,
+                                       NestedNameSpecifier Qualifier,
+                                       SourceLocation NameLoc,
+                                       bool InFunctionPrototype = false);
 
   /// Rebuilds a type within the context of the current instantiation.
   ///
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 47e8106fd9de8..39a23736c41a6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18170,11 +18170,11 @@ static bool isAcceptableTagRedeclContext(Sema &S, 
DeclContext *OldDC,
   return false;
 }
 
-QualType Sema::BuildMSVCEnumTypedefType(NamedDecl *Found,
-                                        ElaboratedTypeKeyword Keyword,
-                                        NestedNameSpecifier Qualifier,
-                                        SourceLocation NameLoc,
-                                        bool InFunctionPrototype) {
+QualType Sema::TryBuildMSVCEnumTypedefType(NamedDecl *Found,
+                                           ElaboratedTypeKeyword Keyword,
+                                           NestedNameSpecifier Qualifier,
+                                           SourceLocation NameLoc,
+                                           bool InFunctionPrototype) {
   if (Qualifier)
     Found = Found->getUnderlyingDecl();
   auto *TD = dyn_cast<TypedefNameDecl>(Found);
@@ -18182,11 +18182,12 @@ QualType Sema::BuildMSVCEnumTypedefType(NamedDecl 
*Found,
       InFunctionPrototype || !TD || Keyword != ElaboratedTypeKeyword::Enum ||
       !TD->getUnderlyingType()->isEnumeralType())
     return QualType();
-  if (TagDecl *Tag = TD->getUnderlyingType()->getAsTagDecl())
+  if (TagDecl *Tag = TD->getUnderlyingType()->getAsTagDecl()) {
     if (Tag->getDeclName() == TD->getDeclName() &&
         Tag->getDeclContext()->getRedeclContext()->Equals(
             TD->getDeclContext()->getRedeclContext()))
       return QualType();
+  }
 
   checkTypeDeclType(nullptr, DiagCtorKind::None, TD, NameLoc);
   Diag(NameLoc, diag::ext_ms_enum_typedef) << isa<TypeAliasDecl>(TD);
@@ -18240,8 +18241,8 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
 
     NestedNameSpecifier Qualifier = SS.getScopeRep();
     NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
-    QualType T = BuildMSVCEnumTypedefType(TypeDecl, 
ElaboratedTypeKeyword::Enum,
-                                          Qualifier, NameLoc);
+    QualType T = TryBuildMSVCEnumTypedefType(
+        TypeDecl, ElaboratedTypeKeyword::Enum, Qualifier, NameLoc);
     if (T.isNull())
       return false;
     TypeLocBuilder TLB;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 37864629c1524..6a4f24941260c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1269,7 +1269,7 @@ class TreeTransform {
       case LookupResultKind::FoundOverloaded:
       case LookupResultKind::FoundUnresolvedValue: {
         NamedDecl *SomeDecl = Result.getRepresentativeDecl();
-        if (QualType T = SemaRef.BuildMSVCEnumTypedefType(
+        if (QualType T = SemaRef.TryBuildMSVCEnumTypedefType(
                 SomeDecl, Keyword, QualifierLoc.getNestedNameSpecifier(), 
IdLoc,
                 getDerived().isTransformingFunctionParameterType());
             !T.isNull())
diff --git a/clang/test/SemaCXX/ms-enum-typedef.cpp 
b/clang/test/SemaCXX/ms-enum-typedef.cpp
index 5e9c77782067a..7e2cc3409db68 100644
--- a/clang/test/SemaCXX/ms-enum-typedef.cpp
+++ b/clang/test/SemaCXX/ms-enum-typedef.cpp
@@ -18,7 +18,7 @@ struct StandardUsingCase {
   enum SameName *member;
 };
 
-typedef enum FriendNamed { FriendValue } FriendAlias; // expected-note 
{{declared here}}
+typedef enum FriendNamed { FriendValue } FriendAlias; // #FriendAliasDecl
 typedef enum AfterFriendNamed { AfterFriendValue } AfterFriendAlias; // 
nomsvc-note {{declared here}}
 typedef enum TemplateFriendNamed { TemplateFriendValue } TemplateFriendAlias; 
// expected-note {{declared here}}
 typedef enum AfterTemplateFriendNamed { AfterTemplateFriendValue } 
AfterTemplateFriendAlias; // nomsvc-note {{declared here}}
@@ -30,7 +30,9 @@ typedef enum NestedInnerNamed { NestedInnerValue } 
NestedInnerAlias; // nomsvc-n
 typedef enum NestedOuterNamed { NestedOuterValue } NestedOuterAlias; // 
nomsvc-note {{declared here}}
 
 struct FriendUse {
-  friend void friend_alias(enum FriendAlias *); // expected-error {{typedef 
'FriendAlias' cannot be referenced with the 'enum' specifier}}
+  friend void friend_alias(enum FriendAlias *);
+  // expected-error@-1 {{typedef 'FriendAlias' cannot be referenced with the 
'enum' specifier}}
+  // expected-note@#FriendAliasDecl {{declared here}}
   enum AfterFriendAlias *member; // nomsvc-error {{typedef 'AfterFriendAlias' 
cannot be referenced with the 'enum' specifier}}
                                  // msvc-warning@-1 {{using an 'enum' 
specifier with a typedef name is a Microsoft extension}}
 };

>From 1dba6cddaedc0224658f10527daa89f092e9e091 Mon Sep 17 00:00:00 2001
From: Zachary Henkel <[email protected]>
Date: Thu, 24 Sep 2026 09:13:05 -0700
Subject: [PATCH 5/7] [clang] Simplify enum typedef eligibility checks

**AI-authored commit** - Code change and wording were produced by an AI agent; 
reviewed by zahen.

Split the mode, context, and target checks in TryBuildMSVCEnumTypedefType.

Co-authored-by: Copilot <[email protected]>
Copilot-Session: f1668d1f-2a77-45bf-87ce-93f4647a5386
---
 clang/lib/Sema/SemaDecl.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 39a23736c41a6..497f372177faf 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18175,12 +18175,15 @@ QualType Sema::TryBuildMSVCEnumTypedefType(NamedDecl 
*Found,
                                            NestedNameSpecifier Qualifier,
                                            SourceLocation NameLoc,
                                            bool InFunctionPrototype) {
+  if (!getLangOpts().CPlusPlus || !getLangOpts().MSVCCompat)
+    return QualType();
+  if (InFunctionPrototype || Keyword != ElaboratedTypeKeyword::Enum)
+    return QualType();
+
   if (Qualifier)
     Found = Found->getUnderlyingDecl();
   auto *TD = dyn_cast<TypedefNameDecl>(Found);
-  if (!getLangOpts().CPlusPlus || !getLangOpts().MSVCCompat ||
-      InFunctionPrototype || !TD || Keyword != ElaboratedTypeKeyword::Enum ||
-      !TD->getUnderlyingType()->isEnumeralType())
+  if (!TD || !TD->getUnderlyingType()->isEnumeralType())
     return QualType();
   if (TagDecl *Tag = TD->getUnderlyingType()->getAsTagDecl()) {
     if (Tag->getDeclName() == TD->getDeclName() &&

>From 94841e9126c05f2dff3f2f769d110bc37daaf5f9 Mon Sep 17 00:00:00 2001
From: Zachary Henkel <[email protected]>
Date: Thu, 24 Sep 2026 09:14:22 -0700
Subject: [PATCH 6/7] [clang] Avoid unnecessary enum typedef lookup work

**AI-authored commit** - Code change and wording were produced by an AI agent; 
reviewed by zahen.

Check language mode and tag-use state before unwrapping and casting lookup 
results.

Co-authored-by: Copilot <[email protected]>
Copilot-Session: f1668d1f-2a77-45bf-87ce-93f4647a5386
---
 clang/lib/Sema/SemaDecl.cpp | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 497f372177faf..93dc703e3fccc 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18219,13 +18219,17 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
   bool ScopedEnum = ScopedEnumKWLoc.isValid();
 
   auto SetMSVCEnumType = [&](NamedDecl *Found) {
+    if (!getLangOpts().CPlusPlus || !getLangOpts().MSVCCompat ||
+        !MSVCEnumType || Kind != TagTypeKind::Enum ||
+        TUK != TagUseKind::Reference || (S && S->containedInPrototypeScope()))
+      return false;
+
     NamedDecl *TypeDecl = Found;
     if (!SS.isEmpty() ||
         (isa<UsingShadowDecl>(Found) && Found->getDeclContext()->isRecord()))
       TypeDecl = Found->getUnderlyingDecl();
     auto *TD = dyn_cast<TypedefNameDecl>(TypeDecl);
-    if (!MSVCEnumType || !TD || Kind != TagTypeKind::Enum ||
-        TUK != TagUseKind::Reference || (S && S->containedInPrototypeScope()))
+    if (!TD)
       return false;
     // MSVC accepts an unqualified typedef only in class scope and not after a
     // friend specifier.

>From 6db5aa9c2c52937b86f9f13a873e6222059eb760 Mon Sep 17 00:00:00 2001
From: Zachary Henkel <[email protected]>
Date: Thu, 24 Sep 2026 09:18:05 -0700
Subject: [PATCH 7/7] [clang] Return parsed enum typedef types from lookup

**AI-authored commit** - Code change and wording were produced by an AI agent; 
reviewed by zahen.

Return the parsed type from TryBuildMSVCEnumType and assign the output at its 
call site.

Co-authored-by: Copilot <[email protected]>
Copilot-Session: f1668d1f-2a77-45bf-87ce-93f4647a5386
---
 clang/lib/Sema/SemaDecl.cpp | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 93dc703e3fccc..07030b7e8b659 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18218,11 +18218,11 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
   TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
   bool ScopedEnum = ScopedEnumKWLoc.isValid();
 
-  auto SetMSVCEnumType = [&](NamedDecl *Found) {
+  auto TryBuildMSVCEnumType = [&](NamedDecl *Found) -> ParsedType {
     if (!getLangOpts().CPlusPlus || !getLangOpts().MSVCCompat ||
         !MSVCEnumType || Kind != TagTypeKind::Enum ||
         TUK != TagUseKind::Reference || (S && S->containedInPrototypeScope()))
-      return false;
+      return {};
 
     NamedDecl *TypeDecl = Found;
     if (!SS.isEmpty() ||
@@ -18230,20 +18230,20 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
       TypeDecl = Found->getUnderlyingDecl();
     auto *TD = dyn_cast<TypedefNameDecl>(TypeDecl);
     if (!TD)
-      return false;
+      return {};
     // MSVC accepts an unqualified typedef only in class scope and not after a
     // friend specifier.
     if (SS.isEmpty()) {
       if (IsFriend)
-        return false;
+        return {};
       auto *CurRecord = dyn_cast<CXXRecordDecl>(CurContext);
       if (!CurRecord)
-        return false;
+        return {};
       DeclContext *TDContext = TD->getDeclContext();
       auto *TDRecord = dyn_cast<CXXRecordDecl>(TDContext);
       if (!TDContext->Encloses(CurContext) &&
           !(TDRecord && CurRecord->isDerivedFrom(TDRecord)))
-        return false;
+        return {};
     }
 
     NestedNameSpecifier Qualifier = SS.getScopeRep();
@@ -18251,11 +18251,10 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
     QualType T = TryBuildMSVCEnumTypedefType(
         TypeDecl, ElaboratedTypeKeyword::Enum, Qualifier, NameLoc);
     if (T.isNull())
-      return false;
+      return {};
     TypeLocBuilder TLB;
     TLB.push<TypedefTypeLoc>(T).set(KWLoc, QualifierLoc, NameLoc);
-    *MSVCEnumType = CreateParsedType(T, TLB.getTypeSourceInfo(Context, T));
-    return true;
+    return CreateParsedType(T, TLB.getTypeSourceInfo(Context, T));
   };
 
   // FIXME: Check member specializations more carefully.
@@ -18726,8 +18725,10 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
     // okay according to the likely resolution of an open issue;
     // see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#407
     if (getLangOpts().CPlusPlus) {
-      if (SetMSVCEnumType(DirectPrevDecl))
+      if (ParsedType BuiltType = TryBuildMSVCEnumType(DirectPrevDecl)) {
+        *MSVCEnumType = BuiltType;
         return (Decl *)nullptr;
+      }
       if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(PrevDecl)) {
         if (TagDecl *Tag = TD->getUnderlyingType()->getAsTagDecl()) {
           if (Tag->getDeclName() == Name &&

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

Reply via email to