Skip to content

Commit

Permalink
Add -fgnuc-version= to control __GNUC__ and other GCC macros
Browse files Browse the repository at this point in the history
I noticed that compiling on Windows with -fno-ms-compatibility had the
side effect of defining __GNUC__, along with __GNUG__, __GXX_RTTI__, and
a number of other macros for GCC compatibility. This is undesirable and
causes Chromium to do things like mix __attribute__ and __declspec,
which doesn't work. We should have a positive language option to enable
GCC compatibility features so that we can experiment with
-fno-ms-compatibility on Windows. This change adds -fgnuc-version= to be
that option.

My issue aside, users have, for a long time, reported that __GNUC__
doesn't match their expectations in one way or another. We have
encouraged users to migrate code away from this macro, but new code
continues to be written assuming a GCC-only environment. There's really
nothing we can do to stop that. By adding this flag, we can allow them
to choose their own adventure with __GNUC__.

This overlaps a bit with the "GNUMode" language option from -std=gnu*.
The gnu language mode tends to enable non-conforming behaviors that we'd
rather not enable by default, but the we want to set things like
__GXX_RTTI__ by default, so I've kept these separate.

Helps address PR42817

Reviewed By: hans, nickdesaulniers, MaskRay

Differential Revision: https://reviews.llvm.org/D68055

llvm-svn: 374449
  • Loading branch information
rnk committed Oct 10, 2019
1 parent 53a53e6 commit 5e866e4
Show file tree
Hide file tree
Showing 14 changed files with 190 additions and 105 deletions.
5 changes: 4 additions & 1 deletion clang/docs/ReleaseNotes.rst
Expand Up @@ -80,7 +80,10 @@ Non-comprehensive list of changes in this release
New Compiler Flags
------------------

- ...
- The -fgnuc-version= flag now controls the value of ``__GNUC__`` and related
macros. This flag does not enable or disable any GCC extensions implemented in
Clang. Setting the version to zero causes Clang to leave ``__GNUC__`` and
other GNU-namespaced macros, such as ``__GXX_WEAK__``, undefined.

Deprecated Compiler Flags
-------------------------
Expand Down
7 changes: 7 additions & 0 deletions clang/docs/UsersManual.rst
Expand Up @@ -701,6 +701,13 @@ Other Options
-------------
Clang options that don't fit neatly into other categories.

.. option:: -fgnuc-version=

This flag controls the value of ``__GNUC__`` and related macros. This flag
does not enable or disable any GCC extensions implemented in Clang. Setting
the version to zero causes Clang to leave ``__GNUC__`` and other
GNU-namespaced macros, such as ``__GXX_WEAK__``, undefined.

.. option:: -MV

When emitting a dependency file, use formatting conventions appropriate
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Expand Up @@ -111,6 +111,7 @@ BENIGN_LANGOPT(DollarIdents , 1, 1, "'$' in identifiers")
BENIGN_LANGOPT(AsmPreprocessor, 1, 0, "preprocessor in asm mode")
LANGOPT(GNUMode , 1, 1, "GNU extensions")
LANGOPT(GNUKeywords , 1, 1, "GNU keywords")
VALUE_LANGOPT(GNUCVersion , 32, 0, "GNU C compatibility version")
BENIGN_LANGOPT(ImplicitInt, 1, !C99 && !CPlusPlus, "C89 implicit 'int'")
LANGOPT(Digraphs , 1, 0, "digraphs")
BENIGN_LANGOPT(HexFloats , 1, C99, "C99 hexadecimal float constants")
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -1187,6 +1187,9 @@ def fno_use_line_directives : Flag<["-"], "fno-use-line-directives">, Group<f_Gr

def ffreestanding : Flag<["-"], "ffreestanding">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Assert that the compilation takes place in a freestanding environment">;
def fgnuc_version_EQ : Joined<["-"], "fgnuc-version=">, Group<f_Group>,
HelpText<"Sets various macros to claim compatibility with the given GCC version (default is 4.2.1)">,
Flags<[CC1Option, CoreOption]>;
def fgnu_keywords : Flag<["-"], "fgnu-keywords">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Allow GNU-extension keywords regardless of language standard">;
def fgnu89_inline : Flag<["-"], "fgnu89-inline">, Group<f_Group>, Flags<[CC1Option]>,
Expand Down
33 changes: 28 additions & 5 deletions clang/lib/Driver/ToolChains/Clang.cpp
Expand Up @@ -4875,13 +4875,36 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-fuse-line-directives");

// -fms-compatibility=0 is default.
if (Args.hasFlag(options::OPT_fms_compatibility,
options::OPT_fno_ms_compatibility,
(IsWindowsMSVC &&
Args.hasFlag(options::OPT_fms_extensions,
options::OPT_fno_ms_extensions, true))))
bool IsMSVCCompat = Args.hasFlag(
options::OPT_fms_compatibility, options::OPT_fno_ms_compatibility,
(IsWindowsMSVC && Args.hasFlag(options::OPT_fms_extensions,
options::OPT_fno_ms_extensions, true)));
if (IsMSVCCompat)
CmdArgs.push_back("-fms-compatibility");

// Handle -fgcc-version, if present.
VersionTuple GNUCVer;
if (Arg *A = Args.getLastArg(options::OPT_fgnuc_version_EQ)) {
// Check that the version has 1 to 3 components and the minor and patch
// versions fit in two decimal digits.
StringRef Val = A->getValue();
Val = Val.empty() ? "0" : Val; // Treat "" as 0 or disable.
bool Invalid = GNUCVer.tryParse(Val);
unsigned Minor = GNUCVer.getMinor().getValueOr(0);
unsigned Patch = GNUCVer.getSubminor().getValueOr(0);
if (Invalid || GNUCVer.getBuild() || Minor >= 100 || Patch >= 100) {
D.Diag(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
}
} else if (!IsMSVCCompat) {
// Imitate GCC 4.2.1 by default if -fms-compatibility is not in effect.
GNUCVer = VersionTuple(4, 2, 1);
}
if (!GNUCVer.empty()) {
CmdArgs.push_back(
Args.MakeArgString("-fgnuc-version=" + GNUCVer.getAsString()));
}

VersionTuple MSVT = TC.computeMSVCVersion(&D, Args);
if (!MSVT.empty())
CmdArgs.push_back(
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Expand Up @@ -2250,6 +2250,7 @@ void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
Opts.Digraphs = Std.hasDigraphs();
Opts.GNUMode = Std.isGNUMode();
Opts.GNUInline = !Opts.C99 && !Opts.CPlusPlus;
Opts.GNUCVersion = 0;
Opts.HexFloats = Std.hasHexFloats();
Opts.ImplicitInt = Std.hasImplicitInt();

Expand Down Expand Up @@ -2574,6 +2575,21 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
(Opts.ObjCRuntime.getKind() == ObjCRuntime::FragileMacOSX);
}

if (Arg *A = Args.getLastArg(options::OPT_fgnuc_version_EQ)) {
// Check that the version has 1 to 3 components and the minor and patch
// versions fit in two decimal digits.
VersionTuple GNUCVer;
bool Invalid = GNUCVer.tryParse(A->getValue());
unsigned Major = GNUCVer.getMajor();
unsigned Minor = GNUCVer.getMinor().getValueOr(0);
unsigned Patch = GNUCVer.getSubminor().getValueOr(0);
if (Invalid || GNUCVer.getBuild() || Minor >= 100 || Patch >= 100) {
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
}
Opts.GNUCVersion = Major * 100 * 100 + Minor * 100 + Patch;
}

if (Args.hasArg(OPT_fgnu89_inline)) {
if (Opts.CPlusPlus)
Diags.Report(diag::err_drv_argument_not_allowed_with)
Expand Down
34 changes: 20 additions & 14 deletions clang/lib/Frontend/InitPreprocessor.cpp
Expand Up @@ -574,13 +574,22 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
Builder.defineMacro("__clang_version__",
"\"" CLANG_VERSION_STRING " "
+ getClangFullRepositoryVersion() + "\"");
if (!LangOpts.MSVCCompat) {
// Currently claim to be compatible with GCC 4.2.1-5621, but only if we're
// not compiling for MSVC compatibility
Builder.defineMacro("__GNUC_MINOR__", "2");
Builder.defineMacro("__GNUC_PATCHLEVEL__", "1");
Builder.defineMacro("__GNUC__", "4");

if (LangOpts.GNUCVersion != 0) {
// Major, minor, patch, are given two decimal places each, so 4.2.1 becomes
// 40201.
unsigned GNUCMajor = LangOpts.GNUCVersion / 100 / 100;
unsigned GNUCMinor = LangOpts.GNUCVersion / 100 % 100;
unsigned GNUCPatch = LangOpts.GNUCVersion % 100;
Builder.defineMacro("__GNUC__", Twine(GNUCMajor));
Builder.defineMacro("__GNUC_MINOR__", Twine(GNUCMinor));
Builder.defineMacro("__GNUC_PATCHLEVEL__", Twine(GNUCPatch));
Builder.defineMacro("__GXX_ABI_VERSION", "1002");

if (LangOpts.CPlusPlus) {
Builder.defineMacro("__GNUG__", Twine(GNUCMajor));
Builder.defineMacro("__GXX_WEAK__");
}
}

// Define macros for the C11 / C++11 memory orderings
Expand Down Expand Up @@ -619,7 +628,7 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
if (!LangOpts.GNUMode && !LangOpts.MSVCCompat)
Builder.defineMacro("__STRICT_ANSI__");

if (!LangOpts.MSVCCompat && LangOpts.CPlusPlus11)
if (LangOpts.GNUCVersion && LangOpts.CPlusPlus11)
Builder.defineMacro("__GXX_EXPERIMENTAL_CXX0X__");

if (LangOpts.ObjC) {
Expand Down Expand Up @@ -699,7 +708,7 @@ static void InitializePredefinedMacros(const TargetInfo &TI,

if (!LangOpts.MSVCCompat && LangOpts.Exceptions)
Builder.defineMacro("__EXCEPTIONS");
if (!LangOpts.MSVCCompat && LangOpts.RTTI)
if (LangOpts.GNUCVersion && LangOpts.RTTI)
Builder.defineMacro("__GXX_RTTI");

if (LangOpts.SjLjExceptions)
Expand All @@ -713,11 +722,8 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
if (LangOpts.Deprecated)
Builder.defineMacro("__DEPRECATED");

if (!LangOpts.MSVCCompat && LangOpts.CPlusPlus) {
Builder.defineMacro("__GNUG__", "4");
Builder.defineMacro("__GXX_WEAK__");
if (!LangOpts.MSVCCompat && LangOpts.CPlusPlus)
Builder.defineMacro("__private_extern__", "extern");
}

if (LangOpts.MicrosoftExt) {
if (LangOpts.WChar) {
Expand Down Expand Up @@ -927,7 +933,7 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
else
Builder.defineMacro("__FINITE_MATH_ONLY__", "0");

if (!LangOpts.MSVCCompat) {
if (LangOpts.GNUCVersion) {
if (LangOpts.GNUInline || LangOpts.CPlusPlus)
Builder.defineMacro("__GNUC_GNU_INLINE__");
else
Expand Down Expand Up @@ -964,7 +970,7 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
#undef DEFINE_LOCK_FREE_MACRO
};
addLockFreeMacros("__CLANG_ATOMIC_");
if (!LangOpts.MSVCCompat)
if (LangOpts.GNUCVersion)
addLockFreeMacros("__GCC_ATOMIC_");

if (LangOpts.NoInlineDefine)
Expand Down
26 changes: 26 additions & 0 deletions clang/test/Driver/fgnuc-version.c
@@ -0,0 +1,26 @@
//
// Verify -fgnuc-version parsing
//

// RUN: %clang -c %s -target i686-linux -### 2>&1 | FileCheck %s -check-prefix GNUC-DEFAULT
// GNUC-DEFAULT: "-fgnuc-version=4.2.1"

// RUN: %clang -c %s -target i686-linux -fgnuc-version=100.99.99 -### 2>&1 | FileCheck %s -check-prefix GNUC-OVERRIDE
// GNUC-OVERRIDE: "-fgnuc-version=100.99.99"

// RUN: %clang -c %s -target i686-linux -fgnuc-version=0 -### 2>&1 | FileCheck %s -check-prefix GNUC-DISABLE
// RUN: %clang -c %s -target i686-linux -fgnuc-version= -### 2>&1 | FileCheck %s -check-prefix GNUC-DISABLE
// GNUC-DISABLE-NOT: "-fgnuc-version=

// RUN: not %clang -c %s -target i686-linux -fgnuc-version=100.100.10 2>&1 | FileCheck %s -check-prefix GNUC-INVALID
// RUN: not %clang -c %s -target i686-linux -fgnuc-version=100.10.100 2>&1 | FileCheck %s -check-prefix GNUC-INVALID
// RUN: not %clang -c %s -target i686-linux -fgnuc-version=-1.0.0 2>&1 | FileCheck %s -check-prefix GNUC-INVALID
// GNUC-INVALID: error: invalid value {{.*}} in '-fgnuc-version={{.*}}'

// RUN: %clang -fgnuc-version=100.99.99 %s -dM -E -o - | FileCheck %s -check-prefix GNUC-LARGE
// GNUC-LARGE: #define __GNUC_MINOR__ 99
// GNUC-LARGE: #define __GNUC_PATCHLEVEL__ 99
// GNUC-LARGE: #define __GNUC__ 100

// RUN: %clang -fgnuc-version=100.99.99 -x c++ %s -dM -E -o - | FileCheck %s -check-prefix GXX-LARGE
// GXX-LARGE: #define __GNUG__ 100
6 changes: 3 additions & 3 deletions clang/test/Driver/rewrite-legacy-objc.m
Expand Up @@ -3,11 +3,11 @@
// TEST0: clang{{.*}}" "-cc1"
// TEST0: "-rewrite-objc"
// FIXME: CHECK-NOT is broken somehow, it doesn't work here. Check adjacency instead.
// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx-fragile" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fmax-type-align=16" "-fdiagnostics-show-option"
// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fgnuc-version=4.2.1" "-fobjc-runtime=macosx-fragile" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fmax-type-align=16" "-fdiagnostics-show-option"
// TEST0: rewrite-legacy-objc.m"
// RUN: %clang -no-canonical-prefixes -target i386-apple-macosx10.9.0 -rewrite-legacy-objc %s -o - -### 2>&1 | \
// RUN: FileCheck -check-prefix=TEST1 %s
// RUN: %clang -no-canonical-prefixes -target i386-apple-macosx10.6.0 -rewrite-legacy-objc %s -o - -### 2>&1 | \
// RUN: FileCheck -check-prefix=TEST2 %s
// TEST1: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx-fragile" "-fobjc-subscripting-legacy-runtime" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option"
// TEST2: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx-fragile" "-fobjc-subscripting-legacy-runtime" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option"
// TEST1: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fgnuc-version=4.2.1" "-fobjc-runtime=macosx-fragile" "-fobjc-subscripting-legacy-runtime" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option"
// TEST2: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fgnuc-version=4.2.1" "-fobjc-runtime=macosx-fragile" "-fobjc-subscripting-legacy-runtime" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option"
2 changes: 1 addition & 1 deletion clang/test/Driver/rewrite-objc.m
Expand Up @@ -3,4 +3,4 @@
// TEST0: clang{{.*}}" "-cc1"
// TEST0: "-rewrite-objc"
// FIXME: CHECK-NOT is broken somehow, it doesn't work here. Check adjacency instead.
// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fmax-type-align=16" "-fdiagnostics-show-option"
// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fgnuc-version=4.2.1" "-fobjc-runtime=macosx" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fmax-type-align=16" "-fdiagnostics-show-option"
12 changes: 6 additions & 6 deletions clang/test/Frontend/gnu-inline.c
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -std=c89 -fsyntax-only -x c -E -dM %s | FileCheck --check-prefix=GNU-INLINE %s
// RUN: %clang_cc1 -std=c99 -fsyntax-only -x c -E -dM %s | FileCheck --check-prefix=STDC-INLINE %s
// RUN: %clang_cc1 -std=c99 -fgnu89-inline -fsyntax-only -x c -E -dM %s | FileCheck --check-prefix=GNU-INLINE %s
// RUN: %clang_cc1 -fsyntax-only -x c++ -E -dM %s | FileCheck --check-prefix=GNU-INLINE %s
// RUN: not %clang_cc1 -fgnu89-inline -fsyntax-only -x c++ %s 2>&1 | FileCheck --check-prefix=CXX %s
// RUN: not %clang_cc1 -fgnu89-inline -fsyntax-only -x objective-c++ %s 2>&1 | FileCheck --check-prefix=OBJCXX %s
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -std=c89 -fsyntax-only -x c -E -dM %s | FileCheck --check-prefix=GNU-INLINE %s
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -std=c99 -fsyntax-only -x c -E -dM %s | FileCheck --check-prefix=STDC-INLINE %s
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -std=c99 -fgnu89-inline -fsyntax-only -x c -E -dM %s | FileCheck --check-prefix=GNU-INLINE %s
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -fsyntax-only -x c++ -E -dM %s | FileCheck --check-prefix=GNU-INLINE %s
// RUN: not %clang_cc1 -fgnu89-inline -fgnuc-version=4.2.1 -fsyntax-only -x c++ %s 2>&1 | FileCheck --check-prefix=CXX %s
// RUN: not %clang_cc1 -fgnu89-inline -fgnuc-version=4.2.1 -fsyntax-only -x objective-c++ %s 2>&1 | FileCheck --check-prefix=OBJCXX %s

// CXX: '-fgnu89-inline' not allowed with 'C++'
// OBJCXX: '-fgnu89-inline' not allowed with 'Objective-C++'
Expand Down
8 changes: 4 additions & 4 deletions clang/test/Headers/stdbool.cpp
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -std=gnu++98 -E -dM %s | FileCheck --check-prefix=CHECK-GNU-COMPAT-98 %s
// RUN: %clang_cc1 -std=gnu++11 -E -dM %s | FileCheck --check-prefix=CHECK-GNU-COMPAT-11 %s
// RUN: %clang_cc1 -std=c++98 -E -dM %s | FileCheck --check-prefix=CHECK-CONFORMING %s
// RUN: %clang_cc1 -fsyntax-only -std=gnu++98 -verify -Weverything %s
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -std=gnu++98 -E -dM %s | FileCheck --check-prefix=CHECK-GNU-COMPAT-98 %s
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -std=gnu++11 -E -dM %s | FileCheck --check-prefix=CHECK-GNU-COMPAT-11 %s
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -std=c++98 -E -dM %s | FileCheck --check-prefix=CHECK-CONFORMING %s
// RUN: %clang_cc1 -fgnuc-version=4.2.1 -fsyntax-only -std=gnu++98 -verify -Weverything %s
#include <stdbool.h>
#define zzz

Expand Down

0 comments on commit 5e866e4

Please sign in to comment.