Author: Riyaz Ahmad
Date: 2026-01-30T14:24:43+05:30
New Revision: c391efe6fb67329d8e2fd231692cc6b0ea902956

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

LOG: [Driver][Frontend] Add -f[no-]ms-anonymous-structs flag to control 
Microsoft anonymous struct/union extension                                      
                                                                                
 (#176551)

Add a Clang driver option -fms-anonymous-structs and
-fno-ms-anonymous-structs
to enable or disable Microsoft anonymous struct/union support
independently of -fms-extensions.

**Motivation**:
- On some platforms (e.g. AIX), enabling `-fms-extensions` can conflict
    with system headers (such as usage of `__ptr32`).
  - Some codebases rely specifically on Microsoft anonymous struct/union
    behavior without requiring other Microsoft extensions.

This change allows users to selectively enable the anonymous
struct/union
extension at the driver level without enabling full Microsoft
compatibility
mode.

**Behavior**:
  - -fms-anonymous-structs enables the feature explicitly.
- The feature is implicitly enabled by `-fms-extensions `or
`-fms-compatibility`.
- When multiple flags are present, the last flag on the command line
wins.
    For example (last option wins):
`-fms-extensions` `-fno-ms-anonymous-structs` -> Disable the feature.
` -fno-ms-anonymous-structs` `-fms-anonymous-structs` -> Enables the
feature.
   `-fno-ms-anonymous-structs`` -fms-extensions` -> Enables the feature.

Note: When using CC1 directly (clang -cc1) or -Xclang, explicit
`-f[no-]ms-anonymous-structs` flags
take precedence over implicit enablement from -fms-extensions or
-fms-compatibility.
For example: 
` -Xclang -fno-ms-anonymous-structs -Xclang -fms-extensions `-> Disable
the feature.

**Implementation**:
  - Added driver-level options `-fms-anonymous-structs `and
    `-fno-ms-anonymous-structs`.
  - Forwarded the option from the driver to CC1.
  - Added driver and frontend tests to verify option interactions.

---------

Co-authored-by: Riyaz Ahmad <[email protected]>

Added: 
    clang/test/Driver/ms-anonymous-structs.c
    clang/test/Frontend/ms-anon-structs-args.c
    clang/test/Sema/MicrosoftAnonymousStructs.c

Modified: 
    clang/docs/LanguageExtensions.rst
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Options/Options.td
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Sema/SemaDecl.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index 0adfaebf24581..29328355c3e6f 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -2468,6 +2468,50 @@ Clang provides support for Microsoft extensions to 
support enumerations with no
 
   typedef enum empty { } A;
 
+Microsoft Anonymous Structs and Unions
+--------------------------------------
+
+Clang provides support for a Microsoft extension that allows use of named 
struct or union types to
+declare anonymous members inside another struct or union, making their fields 
directly accessible
+from the enclosing type.
+
+For example, consider the following code:
+
+.. code-block:: c
+
+    struct Inner {
+        int x;
+        int y;
+    };
+
+    struct Outer {
+        struct Inner;  /* Microsoft extension: named anonymous struct member */
+    };
+
+    void f(struct Outer *o) {
+        o->x = 1;      /* accesses x member of anonymous member of type Inner 
directly */
+        o->y = 1;      /* accesses x member of anonymous member of type Inner 
directly */
+    }
+
+Without this extension, such declarations generate a warning that the 
declaration does not
+declare anything, the associated member names are not available for access, 
and the layout
+of types containing such declarations are affected accordingly.
+
+This extension can be controlled independently of other Microsoft extensions:
+
+* ``-fms-anonymous-structs``
+    Enable named anonymous struct/union support
+
+* ``-fno-ms-anonymous-structs``
+    Disable anonymous struct/union support
+
+This extension is also **implicitly enabled** when either of the following 
options is used:
+
+* ``-fms-extensions``
+* ``-fms-compatibility``
+
+When multiple controlling options are specified, the last option on the 
command line takes
+precedence.
 
 Interoperability with C++11 lambdas
 -----------------------------------

diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 81abc9adc3bf0..bb40cff50b8f0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -121,6 +121,9 @@ Non-comprehensive list of changes in this release
 
 New Compiler Flags
 ------------------
+- New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added
+  to enable or disable Microsoft's anonymous struct/union extension without
+  enabling other ``-fms-extensions`` features (#GH177607).
 
 Deprecated Compiler Flags
 -------------------------

diff  --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index a86394aa44f6b..ba12e522f331f 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -47,6 +47,7 @@ LANGOPT(MSVCCompat        , 1, 0, NotCompatible, "Microsoft 
Visual C++ full comp
 LANGOPT(Kernel            , 1, 0, NotCompatible, "Kernel mode")
 LANGOPT(MicrosoftExt      , 1, 0, NotCompatible, "Microsoft C++ extensions")
 LANGOPT(ZOSExt            , 1, 0, NotCompatible, "z/OS extensions")
+LANGOPT(MSAnonymousStructs, 1, 0, NotCompatible, "Microsoft anonymous struct 
and union extension")
 LANGOPT(AsmBlocks         , 1, 0, NotCompatible, "Microsoft inline asm blocks")
 LANGOPT(Borland           , 1, 0, NotCompatible, "Borland extensions")
 LANGOPT(CPlusPlus         , 1, 0, NotCompatible, "C++")

diff  --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index bcac5ff937d7b..41a23ba4cb33d 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3329,6 +3329,14 @@ def fms_extensions : Flag<["-"], "fms-extensions">, 
Group<f_Group>,
   Visibility<[ClangOption, CC1Option, CLOption]>,
   HelpText<"Accept some non-standard constructs supported by the Microsoft 
compiler">,
   MarshallingInfoFlag<LangOpts<"MicrosoftExt">>, 
ImpliedByAnyOf<[fms_compatibility.KeyPath]>;
+defm ms_anonymous_structs
+    : BoolFOption<
+          "ms-anonymous-structs", LangOpts<"MSAnonymousStructs">, DefaultFalse,
+          PosFlag<SetTrue, [], [ClangOption, CC1Option],
+                  "Enable Microsoft anonymous struct/union extension.",
+                  [fms_extensions.KeyPath]>,
+          NegFlag<SetFalse, [], [ClangOption, CC1Option],
+                  "Disable Microsoft anonymous struct/union extension.">>;
 defm asm_blocks : BoolFOption<"asm-blocks",
   LangOpts<"AsmBlocks">, Default<fms_extensions.KeyPath>,
   PosFlag<SetTrue, [], [ClangOption, CC1Option]>,

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index af6da80cb5ceb..0293b04217673 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7171,6 +7171,60 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
       CmdArgs.push_back("-fms-define-stdc");
   }
 
+  // -fms-anonymous-structs is disabled by default.
+  // Determine whether to enable Microsoft named anonymous struct/union 
support.
+  // This implements "last flag wins" semantics for -fms-anonymous-structs,
+  // where the feature can be:
+  // - Explicitly enabled via -fms-anonymous-structs.
+  // - Explicitly disabled via fno-ms-anonymous-structs
+  // - Implicitly enabled via -fms-extensions or -fms-compatibility
+  // - Implicitly disabled via -fno-ms-extensions or -fno-ms-compatibility
+  //
+  // When multiple relevent options are present, the last option on the command
+  // line takes precedence. This allows users to selectively override implicit
+  // enablement. Examples:
+  //   -fms-extensions -fno-ms-anonymous-structs -> disabled (explicit 
override)
+  //   -fno-ms-anonymous-structs -fms-extensions -> enabled (last flag wins)
+  auto MSAnonymousStructsOptionToUseOrNull =
+      [](const ArgList &Args) -> const char * {
+    const char *Option = nullptr;
+    constexpr const char *Enable = "-fms-anonymous-structs";
+    constexpr const char *Disable = "-fno-ms-anonymous-structs";
+
+    // Iterate through all arguments in order to implement "last flag wins".
+    for (const Arg *A : Args) {
+      switch (A->getOption().getID()) {
+      case options::OPT_fms_anonymous_structs:
+        A->claim();
+        Option = Enable;
+        break;
+      case options::OPT_fno_ms_anonymous_structs:
+        A->claim();
+        Option = Disable;
+        break;
+      // Each of -fms-extensions and -fms-compatibility implicitly enables the
+      // feature.
+      case options::OPT_fms_extensions:
+      case options::OPT_fms_compatibility:
+        Option = Enable;
+        break;
+      // Each of -fno-ms-extensions and -fno-ms-compatibility implicitly
+      // disables the feature.
+      case options::OPT_fno_ms_extensions:
+      case options::OPT_fno_ms_compatibility:
+        Option = Disable;
+        break;
+      default:
+        break;
+      }
+    }
+    return Option;
+  };
+
+  // Only pass a flag to CC1 if a relevant option was seen
+  if (auto MSAnonOpt = MSAnonymousStructsOptionToUseOrNull(Args))
+    CmdArgs.push_back(MSAnonOpt);
+
   if (Triple.isWindowsMSVCEnvironment() && !D.IsCLMode() &&
       Args.hasArg(options::OPT_fms_runtime_lib_EQ))
     ProcessVSRuntimeLibrary(getToolChain(), Args, CmdArgs);

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 907b7b367f19b..3b2c93b9fe7b5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5342,7 +5342,7 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, 
AccessSpecifier AS,
         DS.getTypeSpecType() == DeclSpec::TST_typename) {
       RecordDecl *Record = Tag ? dyn_cast<RecordDecl>(Tag)
                                : DS.getRepAsType().get()->getAsRecordDecl();
-      if (Record && getLangOpts().MicrosoftExt) {
+      if (Record && getLangOpts().MSAnonymousStructs) {
         Diag(DS.getBeginLoc(), diag::ext_ms_anonymous_record)
             << Record->isUnion() << DS.getSourceRange();
         return BuildMicrosoftCAnonymousStruct(S, DS, Record);

diff  --git a/clang/test/Driver/ms-anonymous-structs.c 
b/clang/test/Driver/ms-anonymous-structs.c
new file mode 100644
index 0000000000000..79723590ee6d4
--- /dev/null
+++ b/clang/test/Driver/ms-anonymous-structs.c
@@ -0,0 +1,38 @@
+// RUN: %clang -### -target powerpc-ibm-aix -fms-anonymous-structs %s 2>&1 | \
+// RUN:   FileCheck %s --check-prefix=MS-STRUCT-ENABLE
+// MS-STRUCT-ENABLE: "-fms-anonymous-structs"
+
+// RUN: %clang -### -target powerpc-ibm-aix -fno-ms-anonymous-structs %s 2>&1 
| \
+// RUN:   FileCheck %s --check-prefix=MS-STRUCT-DISABLE
+// MS-STRUCT-DISABLE: "-fno-ms-anonymous-structs"
+
+// RUN: %clang -### -target powerpc-ibm-aix -fms-extensions %s 2>&1 | \
+// RUN:   FileCheck %s --check-prefix=IMPLICIT-ENABLE
+// IMPLICIT-ENABLE: "-fms-anonymous-structs"
+
+// RUN: %clang -### -target powerpc-ibm-aix -fms-compatibility %s 2>&1 | \
+// RUN:   FileCheck %s --check-prefix=IMPLICIT-ENABLE
+
+// RUN: %clang -### -target powerpc-ibm-aix -fno-ms-anonymous-structs 
-fms-anonymous-structs %s 2>&1 |\
+// RUN:   FileCheck %s --check-prefix=LAST-ENABLE
+// LAST-ENABLE: "-fms-anonymous-structs"
+// LAST-ENABLE-NOT: "-fno-ms-anonymous-structs"
+
+// RUN: %clang -### -target powerpc-ibm-aix -fno-ms-anonymous-structs 
-fms-extensions %s 2>&1 |\
+// RUN:   FileCheck %s --check-prefix=LAST-ENABLE
+
+// RUN: %clang -### -target powerpc-ibm-aix -fms-anonymous-structs 
-fno-ms-anonymous-structs %s 2>&1 |\
+// RUN:   FileCheck %s --check-prefix=LAST-DISABLE
+// LAST-DISABLE: "-fno-ms-anonymous-structs"
+// LAST-DISABLE-NOT: "-fms-anonymous-structs"
+
+// RUN: %clang -### -target powerpc-ibm-aix -fms-extensions 
-fno-ms-anonymous-structs %s 2>&1 |\
+// RUN:   FileCheck %s --check-prefix=LAST-DISABLE
+
+// RUN: %clang -### -target powerpc-ibm-aix -fms-compatibility 
-fno-ms-anonymous-structs %s 2>&1 |\
+// RUN:   FileCheck %s --check-prefix=LAST-DISABLE
+
+// RUN: %clang -### -target powerpc-ibm-aix %s 2>&1 | FileCheck %s 
--check-prefix=NO-MS-STRUCT
+// NO-MS-STRUCT-NOT: "-fms-anonymous-structs"
+// NO-MS-STRUCT-NOT: "-fno-ms-anonymous-structs"
+

diff  --git a/clang/test/Frontend/ms-anon-structs-args.c 
b/clang/test/Frontend/ms-anon-structs-args.c
new file mode 100644
index 0000000000000..571c50d9f3126
--- /dev/null
+++ b/clang/test/Frontend/ms-anon-structs-args.c
@@ -0,0 +1,24 @@
+// Test that -fms-anonymous-structs is a CC1-only option and is accepted by 
CC1 without error.
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fms-anonymous-structs %s 
-fsyntax-only 2>&1 | \
+// RUN:     FileCheck --check-prefix=CC1-OK %s --allow-empty
+// CC1-OK-NOT: error: unknown argument
+
+// Test that multiple occurrences are handled
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fms-anonymous-structs 
-fms-anonymous-structs %s -fsyntax-only 2>&1 | \
+// RUN:     FileCheck --check-prefix=CC1-OK %s --allow-empty
+
+// Test with other MS-related options
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fms-extensions 
-fms-anonymous-structs %s -fsyntax-only 2>&1 | \
+// RUN:     FileCheck --check-prefix=CC1-OK %s --allow-empty
+
+// Test that -fno-ms-anonymous-structs is accepted by CC1 without error.
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fno-ms-anonymous-structs %s 
-fsyntax-only 2>&1 | \
+// RUN:     FileCheck --check-prefix=CC1-OK %s --allow-empty
+
+// Test both orderings of using both the negative and positive forms.
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fms-anonymous-structs 
-fno-ms-anonymous-structs %s -fsyntax-only 2>&1 | \
+// RUN:     FileCheck --check-prefix=CC1-OK %s --allow-empty
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix -fno-ms-anonymous-structs 
-fms-anonymous-structs %s -fsyntax-only 2>&1 | \
+// RUN:     FileCheck --check-prefix=CC1-OK %s --allow-empty

diff  --git a/clang/test/Sema/MicrosoftAnonymousStructs.c 
b/clang/test/Sema/MicrosoftAnonymousStructs.c
new file mode 100644
index 0000000000000..f656411daea3d
--- /dev/null
+++ b/clang/test/Sema/MicrosoftAnonymousStructs.c
@@ -0,0 +1,76 @@
+// RUN: %clang_cc1 -triple i686-windows %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous 
-fms-anonymous-structs
+// RUN: %clang_cc1 -triple powerpc-ibm-aix %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous 
-fms-anonymous-structs
+// RUN: %clang_cc1 -triple i686-windows %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous 
-fms-extensions
+// RUN: %clang_cc1 -triple i686-windows %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous 
-fms-compatibility
+// RUN: %clang_cc1 -triple i686-windows %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous-dis
+// Test that explicit -fno-ms-anonymous-structs does not enable the feature.
+// RUN: %clang_cc1 -triple i686-windows %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous-dis \
+// RUN:   -fno-ms-anonymous-structs
+// Test that explicit -fno-ms-anonymous-structs overrides earlier 
-fms-anonymous-structs.
+// RUN: %clang_cc1 -triple i686-windows %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous-dis \
+// RUN:   -fms-anonymous-structs -fno-ms-anonymous-structs
+// Test that explicit -fms-anonymous-structs overrides earlier 
-fno-ms-anonymous-structs.
+// RUN: %clang_cc1 -triple i686-windows %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous \
+// RUN:   -fno-ms-anonymous-structs -fms-anonymous-structs
+// Test that explicit -fno-ms-anonymous-structs overrides earlier 
-fms-extensions.
+// RUN: %clang_cc1 -triple i686-windows %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous-dis \
+// RUN:   -fms-extensions -fno-ms-anonymous-structs
+// Test that explicit -fno-ms-anonymous-structs overrides earlier 
-fms-compatibility.
+// RUN: %clang_cc1 -triple i686-windows %s -fsyntax-only -Wno-unused-value \
+// RUN:   -Wno-pointer-to-int-cast -Wmicrosoft -verify=ms-anonymous-dis \
+// RUN:   -fms-compatibility -fno-ms-anonymous-structs
+
+struct union_mem {
+  long g;
+};
+
+typedef struct nested1 {
+  long a;
+} NESTED1;
+
+struct nested2 {
+  long b;
+  NESTED1;         // ms-anonymous-warning {{anonymous structs are a Microsoft 
extension}}
+                   // ms-anonymous-dis-warning@-1 {{declaration does not 
declare anything}}
+};
+
+typedef union nested3 {
+  long f;
+  struct union_mem; // ms-anonymous-warning {{anonymous structs are a 
Microsoft extension}}
+                    // ms-anonymous-dis-warning@-1 {{declaration does not 
declare anything}}
+} NESTED3;
+
+struct test {
+  int c;
+  struct nested2;   // ms-anonymous-warning {{anonymous structs are a 
Microsoft extension}}
+                    // ms-anonymous-dis-warning@-1 {{declaration does not 
declare anything}}
+  NESTED3;          // ms-anonymous-warning {{anonymous unions are a Microsoft 
extension}}
+                    // ms-anonymous-dis-warning@-1 {{declaration does not 
declare anything}}
+};
+
+struct nested4 {
+  long d;
+  struct nested5 { // ms-anonymous-warning {{anonymous structs are a Microsoft 
extension}}
+                   // ms-anonymous-dis-warning@-1 {{declaration does not 
declare anything}}
+    long e;
+  };
+};
+
+void foo(void)
+{
+  struct test var;
+  var.c;
+  var.a;          // ms-anonymous-dis-error {{no member named 'a' in 'struct 
test'}}
+  var.b;          // ms-anonymous-dis-error {{no member named 'b' in 'struct 
test'}}
+  var.f;          // ms-anonymous-dis-error {{no member named 'f' in 'struct 
test'}}
+  var.g;          // ms-anonymous-dis-error {{no member named 'g' in 'struct 
test'}}
+}


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

Reply via email to