diff --git a/clang/include/clang/Basic/VersionTuple.h b/clang/include/clang/Basic/VersionTuple.h index 77fd947f96a9..784f3f322d69 100644 --- a/clang/include/clang/Basic/VersionTuple.h +++ b/clang/include/clang/Basic/VersionTuple.h @@ -22,40 +22,48 @@ namespace clang { -/// \brief Represents a version number in the form major[.minor[.subminor]]. +/// \brief Represents a version number in the form major[.minor[.subminor[.build]]]. class VersionTuple { unsigned Major : 31; unsigned Minor : 31; unsigned Subminor : 31; + unsigned Build : 31; unsigned HasMinor : 1; unsigned HasSubminor : 1; + unsigned HasBuild : 1; unsigned UsesUnderscores : 1; public: - VersionTuple() - : Major(0), Minor(0), Subminor(0), HasMinor(false), HasSubminor(false), - UsesUnderscores(false) { } + VersionTuple() + : Major(0), Minor(0), Subminor(0), Build(0), HasMinor(false), + HasSubminor(false), HasBuild(false), UsesUnderscores(false) {} explicit VersionTuple(unsigned Major) - : Major(Major), Minor(0), Subminor(0), HasMinor(false), HasSubminor(false), - UsesUnderscores(false) - { } + : Major(Major), Minor(0), Subminor(0), Build(0), HasMinor(false), + HasSubminor(false), HasBuild(false), UsesUnderscores(false) {} explicit VersionTuple(unsigned Major, unsigned Minor, bool UsesUnderscores = false) - : Major(Major), Minor(Minor), Subminor(0), HasMinor(true), - HasSubminor(false), UsesUnderscores(UsesUnderscores) - { } + : Major(Major), Minor(Minor), Subminor(0), Build(0), HasMinor(true), + HasSubminor(false), HasBuild(false), UsesUnderscores(UsesUnderscores) {} explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor, bool UsesUnderscores = false) - : Major(Major), Minor(Minor), Subminor(Subminor), HasMinor(true), - HasSubminor(true), UsesUnderscores(UsesUnderscores) - { } - + : Major(Major), Minor(Minor), Subminor(Subminor), Build(0), + HasMinor(true), HasSubminor(true), HasBuild(false), + UsesUnderscores(UsesUnderscores) {} + + explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor, + unsigned Build, bool UsesUnderscores = false) + : Major(Major), Minor(Minor), Subminor(Subminor), Build(Build), + HasMinor(true), HasSubminor(true), HasBuild(true), + UsesUnderscores(UsesUnderscores) {} + /// \brief Determine whether this version information is empty /// (e.g., all version components are zero). - bool empty() const { return Major == 0 && Minor == 0 && Subminor == 0; } + bool empty() const { + return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0; + } /// \brief Retrieve the major version number. unsigned getMajor() const { return Major; } @@ -74,6 +82,13 @@ public: return Subminor; } + /// \brief Retrieve the build version number, if provided. + Optional getBuild() const { + if (!HasBuild) + return None; + return Build; + } + bool usesUnderscores() const { return UsesUnderscores; } @@ -85,7 +100,8 @@ public: /// \brief Determine if two version numbers are equivalent. If not /// provided, minor and subminor version numbers are considered to be zero. friend bool operator==(const VersionTuple& X, const VersionTuple &Y) { - return X.Major == Y.Major && X.Minor == Y.Minor && X.Subminor == Y.Subminor; + return X.Major == Y.Major && X.Minor == Y.Minor && + X.Subminor == Y.Subminor && X.Build == Y.Build; } /// \brief Determine if two version numbers are not equivalent. @@ -101,8 +117,8 @@ public: /// If not provided, minor and subminor version numbers are considered to be /// zero. friend bool operator<(const VersionTuple &X, const VersionTuple &Y) { - return std::tie(X.Major, X.Minor, X.Subminor) < - std::tie(Y.Major, Y.Minor, Y.Subminor); + return std::tie(X.Major, X.Minor, X.Subminor, X.Build) < + std::tie(Y.Major, Y.Minor, Y.Subminor, Y.Build); } /// \brief Determine whether one version number follows another. @@ -136,7 +152,7 @@ public: /// \brief Try to parse the given string as a version number. /// \returns \c true if the string does not match the regular expression - /// [0-9]+(\.[0-9]+(\.[0-9]+)) + /// [0-9]+(\.[0-9]+){0,3} bool tryParse(StringRef string); }; diff --git a/clang/include/clang/Driver/CLCompatOptions.td b/clang/include/clang/Driver/CLCompatOptions.td index 254093b17525..1d17fd6d79b3 100644 --- a/clang/include/clang/Driver/CLCompatOptions.td +++ b/clang/include/clang/Driver/CLCompatOptions.td @@ -137,6 +137,12 @@ def _SLASH_vd : CLJoined<"vd">, HelpText<"Control vtordisp placement">, def _SLASH_Zc_strictStrings : CLFlag<"Zc:strictStrings">, HelpText<"Treat string literals as const">, Alias, AliasArgs<["error=c++11-compat-deprecated-writable-strings"]>; +def _SLASH_Zc_threadSafeInit : CLFlag<"Zc:threadSafeInit">, + HelpText<"Enable thread-safe initialization of static variables">, + Alias; +def _SLASH_Zc_threadSafeInit_ : CLFlag<"Zc:threadSafeInit-">, + HelpText<"Disable thread-safe initialization of static variables">, + Alias; def _SLASH_Zc_trigraphs : CLFlag<"Zc:trigraphs">, HelpText<"Enable trigraphs">, Alias; def _SLASH_Zc_trigraphs_off : CLFlag<"Zc:trigraphs-">, diff --git a/clang/lib/Basic/VersionTuple.cpp b/clang/lib/Basic/VersionTuple.cpp index aa43ae298e23..9c73fd98a174 100644 --- a/clang/lib/Basic/VersionTuple.cpp +++ b/clang/lib/Basic/VersionTuple.cpp @@ -32,6 +32,8 @@ raw_ostream& clang::operator<<(raw_ostream &Out, Out << (V.usesUnderscores() ? '_' : '.') << *Minor; if (Optional Subminor = V.getSubminor()) Out << (V.usesUnderscores() ? '_' : '.') << *Subminor; + if (Optional Build = V.getBuild()) + Out << (V.usesUnderscores() ? '_' : '.') << *Build; return Out; } @@ -55,7 +57,7 @@ static bool parseInt(StringRef &input, unsigned &value) { } bool VersionTuple::tryParse(StringRef input) { - unsigned major = 0, minor = 0, micro = 0; + unsigned major = 0, minor = 0, micro = 0, build = 0; // Parse the major version, [0-9]+ if (parseInt(input, major)) return true; @@ -80,9 +82,19 @@ bool VersionTuple::tryParse(StringRef input) { input = input.substr(1); if (parseInt(input, micro)) return true; + if (input.empty()) { + *this = VersionTuple(major, minor, micro); + return false; + } + + // If we're not done, parse the micro version, \.[0-9]+ + if (input[0] != '.') return true; + input = input.substr(1); + if (parseInt(input, build)) return true; + // If we have characters left over, it's an error. if (!input.empty()) return true; - *this = VersionTuple(major, minor, micro); + *this = VersionTuple(major, minor, micro, build); return false; } diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index 0f44ce74b3b4..bcab1bb340a0 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -2483,24 +2483,17 @@ static void addDashXForInput(const ArgList &Args, const InputInfo &Input, CmdArgs.push_back(types::getTypeName(Input.getType())); } -static std::string getMSCompatibilityVersion(const char *VersionStr) { - unsigned Version; - if (StringRef(VersionStr).getAsInteger(10, Version)) - return "0"; - +static VersionTuple getMSCompatibilityVersion(unsigned Version) { if (Version < 100) - return llvm::utostr_32(Version) + ".0"; + return VersionTuple(Version); if (Version < 10000) - return llvm::utostr_32(Version / 100) + "." + - llvm::utostr_32(Version % 100); + return VersionTuple(Version / 100, Version % 100); unsigned Build = 0, Factor = 1; for ( ; Version > 10000; Version = Version / 10, Factor = Factor * 10) Build = Build + (Version % 10) * Factor; - return llvm::utostr_32(Version / 100) + "." + - llvm::utostr_32(Version % 100) + "." + - llvm::utostr_32(Build); + return VersionTuple(Version / 100, Version % 100, Build); } // Claim options we don't want to warn if they are unused. We do this for @@ -4067,11 +4060,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, isSignedCharDefault(getToolChain().getTriple()))) CmdArgs.push_back("-fno-signed-char"); - // -fthreadsafe-static is default. - if (!Args.hasFlag(options::OPT_fthreadsafe_statics, - options::OPT_fno_threadsafe_statics)) - CmdArgs.push_back("-fno-threadsafe-statics"); - // -fuse-cxa-atexit is default. if (!Args.hasFlag(options::OPT_fuse_cxa_atexit, options::OPT_fno_use_cxa_atexit, @@ -4099,9 +4087,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, true)))) CmdArgs.push_back("-fms-compatibility"); - // -fms-compatibility-version=17.00 is default. + // -fms-compatibility-version=18.00 is default. + VersionTuple MSVT; if (Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions, - IsWindowsMSVC) || Args.hasArg(options::OPT_fmsc_version) || + IsWindowsMSVC) || + Args.hasArg(options::OPT_fmsc_version) || Args.hasArg(options::OPT_fms_compatibility_version)) { const Arg *MSCVersion = Args.getLastArg(options::OPT_fmsc_version); const Arg *MSCompatibilityVersion = @@ -4112,16 +4102,23 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, << MSCVersion->getAsString(Args) << MSCompatibilityVersion->getAsString(Args); - std::string Ver; - if (MSCompatibilityVersion) - Ver = Args.getLastArgValue(options::OPT_fms_compatibility_version); - else if (MSCVersion) - Ver = getMSCompatibilityVersion(MSCVersion->getValue()); + if (MSCompatibilityVersion) { + if (MSVT.tryParse(MSCompatibilityVersion->getValue())) + D.Diag(diag::err_drv_invalid_value) + << MSCompatibilityVersion->getAsString(Args) + << MSCompatibilityVersion->getValue(); + } else if (MSCVersion) { + unsigned Version = 0; + if (StringRef(MSCVersion->getValue()).getAsInteger(10, Version)) + D.Diag(diag::err_drv_invalid_value) << MSCVersion->getAsString(Args) + << MSCVersion->getValue(); + MSVT = getMSCompatibilityVersion(Version); + } else { + MSVT = VersionTuple(18); + } - if (Ver.empty()) - CmdArgs.push_back("-fms-compatibility-version=18.00"); - else - CmdArgs.push_back(Args.MakeArgString("-fms-compatibility-version=" + Ver)); + CmdArgs.push_back( + Args.MakeArgString("-fms-compatibility-version=" + MSVT.getAsString())); } // -fno-borland-extensions is default. @@ -4129,6 +4126,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_borland_extensions, false)) CmdArgs.push_back("-fborland-extensions"); + // -fthreadsafe-static is default, except for MSVC compatibility versions less + // than 19. + if (!Args.hasFlag(options::OPT_fthreadsafe_statics, + options::OPT_fno_threadsafe_statics, + !IsWindowsMSVC || MSVT.getMajor() >= 19)) + CmdArgs.push_back("-fno-threadsafe-statics"); + // -fno-delayed-template-parsing is default, except for Windows where MSVC STL // needs it. if (Args.hasFlag(options::OPT_fdelayed_template_parsing, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 42de57eecce9..5d2cdae6e009 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1282,43 +1282,6 @@ static Visibility parseVisibility(Arg *arg, ArgList &args, return DefaultVisibility; } -static unsigned parseMSCVersion(ArgList &Args, DiagnosticsEngine &Diags) { - auto Arg = Args.getLastArg(OPT_fms_compatibility_version); - if (!Arg) - return 0; - - // The MSC versioning scheme involves four versioning components: - // - Major - // - Minor - // - Build - // - Patch - // - // We accept either the old style (_MSC_VER) value, or a _MSC_FULL_VER value. - // Additionally, the value may be provided in the form of a more readable - // MM.mm.bbbbb.pp version. - // - // Unfortunately, due to the bit-width limitations, we cannot currently encode - // the value for the patch level. - - unsigned VC[4] = {0}; - StringRef Value = Arg->getValue(); - SmallVector Components; - - Value.split(Components, ".", llvm::array_lengthof(VC)); - for (unsigned CI = 0, - CE = std::min(Components.size(), llvm::array_lengthof(VC)); - CI < CE; ++CI) { - if (Components[CI].getAsInteger(10, VC[CI])) { - Diags.Report(diag::err_drv_invalid_value) - << Arg->getAsString(Args) << Value; - return 0; - } - } - - // FIXME we cannot encode the patch level - return VC[0] * 10000000 + VC[1] * 100000 + VC[2]; -} - static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, DiagnosticsEngine &Diags) { // FIXME: Cleanup per-file based stuff. @@ -1489,7 +1452,16 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.MSVCCompat = Args.hasArg(OPT_fms_compatibility); Opts.MicrosoftExt = Opts.MSVCCompat || Args.hasArg(OPT_fms_extensions); Opts.AsmBlocks = Args.hasArg(OPT_fasm_blocks) || Opts.MicrosoftExt; - Opts.MSCompatibilityVersion = parseMSCVersion(Args, Diags); + Opts.MSCompatibilityVersion = 0; + if (const Arg *A = Args.getLastArg(OPT_fms_compatibility_version)) { + VersionTuple VT; + if (VT.tryParse(A->getValue())) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) + << A->getValue(); + Opts.MSCompatibilityVersion = VT.getMajor() * 10000000 + + VT.getMinor().getValueOr(0) * 100000 + + VT.getSubminor().getValueOr(0); + } // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs // is specified, or -std is set to a conforming mode. diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c index 2dc810d91e3e..3a22154d6018 100644 --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -317,6 +317,14 @@ // RTTI-NOT: "-fno-rtti-data" // RTTI-NOT: "-fno-rtti" +// thread safe statics are off for versions < 19. +// RUN: %clang_cl /c -### -- %s 2>&1 | FileCheck -check-prefix=NoThreadSafeStatics %s +// RUN: %clang_cl /Zc:threadSafeInit /Zc:threadSafeInit- /c -### -- %s 2>&1 | FileCheck -check-prefix=NoThreadSafeStatics %s +// NoThreadSafeStatics: "-fno-threadsafe-statics" + +// RUN: %clang_cl /Zc:threadSafeInit /c -### -- %s 2>&1 | FileCheck -check-prefix=ThreadSafeStatics %s +// ThreadSafeStatics-NOT: "-fno-threadsafe-statics" + // Accept "core" clang options. // (/Zs is for syntax-only) // RUN: %clang_cl \ diff --git a/clang/test/Driver/msc-version.c b/clang/test/Driver/msc-version.c index 4105eba868fa..18fe731eeba8 100644 --- a/clang/test/Driver/msc-version.c +++ b/clang/test/Driver/msc-version.c @@ -54,7 +54,7 @@ // RUN: %clang -### -target i686-windows -fms-compatibility -fmsc-version=17 -E - &1 | FileCheck %s -check-prefix CHECK-MSC-17 // CHECK-MSC-17-NOT: "-fmsc-version=1700" -// CHECK-MSC-17: "-fms-compatibility-version=17.0" +// CHECK-MSC-17: "-fms-compatibility-version=17" // RUN: %clang -### -target i686-windows -fms-compatibility -fmsc-version=1600 -E - &1 | FileCheck %s -check-prefix CHECK-MSC-16