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
