diff --git a/clang/include/clang/Basic/DarwinSDKInfo.h b/clang/include/clang/Basic/DarwinSDKInfo.h --- a/clang/include/clang/Basic/DarwinSDKInfo.h +++ b/clang/include/clang/Basic/DarwinSDKInfo.h @@ -10,21 +10,132 @@ #define LLVM_CLANG_BASIC_DARWIN_SDK_INFO_H #include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Triple.h" #include "llvm/Support/Error.h" #include "llvm/Support/VersionTuple.h" #include "llvm/Support/VirtualFileSystem.h" +namespace llvm { +namespace json { +class Object; +} // end namespace json +} // end namespace llvm + namespace clang { /// The information about the darwin SDK that was used during this compilation. class DarwinSDKInfo { public: - DarwinSDKInfo(llvm::VersionTuple Version) : Version(Version) {} + /// A value that describes two os-environment pairs that can be used as a key + /// to the version map in the SDK. + struct OSEnvPair { + public: + using StorageType = uint64_t; + + constexpr OSEnvPair(llvm::Triple::OSType FromOS, + llvm::Triple::EnvironmentType FromEnv, + llvm::Triple::OSType ToOS, + llvm::Triple::EnvironmentType ToEnv) + : Value(((uint64_t(FromOS) * uint64_t(llvm::Triple::LastOSType) + + uint64_t(FromEnv)) + << 32ull) | + (uint64_t(ToOS) * uint64_t(llvm::Triple::LastOSType) + + uint64_t(ToEnv))) {} + + /// Returns the os-environment mapping pair that's used to represent the + /// macOS -> Mac Catalyst version mapping. + static inline constexpr OSEnvPair macOStoMacCatalystPair() { + return OSEnvPair(llvm::Triple::MacOSX, llvm::Triple::UnknownEnvironment, + llvm::Triple::IOS, llvm::Triple::MacABI); + } + + private: + StorageType Value; + + friend class DarwinSDKInfo; + }; + + /// Represents a version mapping that maps from a version of one target to a + /// version of a related target. + /// + /// e.g. "macOS_iOSMac":{"10.15":"13.1"} is an example of a macOS -> Mac + /// Catalyst version map. + class RelatedTargetVersionMapping { + public: + RelatedTargetVersionMapping( + VersionTuple MinimumKeyVersion, VersionTuple MaximumKeyVersion, + VersionTuple MinimumValue, VersionTuple MaximumValue, + llvm::DenseMap Mapping) + : MinimumKeyVersion(MinimumKeyVersion), + MaximumKeyVersion(MaximumKeyVersion), MinimumValue(MinimumValue), + MaximumValue(MaximumValue), Mapping(Mapping) { + assert(!this->Mapping.empty() && "unexpected empty mapping"); + } + + /// Returns the value with the lowest version in the mapping. + const VersionTuple &getMinimumValue() const { return MinimumValue; } + + /// Returns the mapped key, or the appropriate Minimum / MaximumValue if + /// they key is outside of the mapping bounds. If they key isn't mapped, but + /// within the minimum and maximum bounds, None is returned. + Optional + map(const VersionTuple &Key, const VersionTuple &MinimumValue, + const Optional &MaximumValue) const; + + static Optional + parseJSON(const llvm::json::Object &Obj, + VersionTuple MaximumDeploymentTarget); + + private: + VersionTuple MinimumKeyVersion; + VersionTuple MaximumKeyVersion; + VersionTuple MinimumValue; + VersionTuple MaximumValue; + llvm::DenseMap Mapping; + }; + + DarwinSDKInfo(VersionTuple Version, VersionTuple MaximumDeploymentTarget, + llvm::DenseMap> + VersionMappings = + llvm::DenseMap>()) + : Version(Version), MaximumDeploymentTarget(MaximumDeploymentTarget), + VersionMappings(std::move(VersionMappings)) {} const llvm::VersionTuple &getVersion() const { return Version; } + // Returns the optional, target-specific version mapping that maps from one + // target to another target. + // + // This mapping is constructed from an appropriate mapping in the SDKSettings, + // for instance, when building for Mac Catalyst, the mapping would contain the + // "macOS_iOSMac" mapping as it maps the macOS versions to the Mac Catalyst + // versions. + // + // This mapping does not exist when the target doesn't have an appropriate + // related version mapping, or when there was an error reading the mapping + // from the SDKSettings, or when it's missing in the SDKSettings. + const RelatedTargetVersionMapping *getVersionMapping(OSEnvPair Kind) const { + auto Mapping = VersionMappings.find(Kind.Value); + if (Mapping == VersionMappings.end()) + return nullptr; + return Mapping->getSecond().hasValue() ? Mapping->getSecond().getPointer() + : nullptr; + } + + static Optional + parseDarwinSDKSettingsJSON(StringRef FilePath, const llvm::json::Object *Obj); + private: - llvm::VersionTuple Version; + VersionTuple Version; + VersionTuple MaximumDeploymentTarget; + // Need to wrap the value in an optional here as the value has to be default + // constructible, and std::unique_ptr doesn't like DarwinSDKInfo being + // Optional as Optional is trying to copy it in emplace. + llvm::DenseMap> + VersionMappings; }; /// Parse the SDK information from the SDKSettings.json file. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -114,6 +114,7 @@ class CodeCompletionTUInfo; class CodeCompletionResult; class CoroutineBodyStmt; + class DarwinSDKInfo; class Decl; class DeclAccessPair; class DeclContext; @@ -1524,6 +1525,8 @@ /// assignment. llvm::DenseMap RefsMinusAssignments; + Optional> CachedDarwinSDKInfo; + public: Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TranslationUnitKind TUKind = TU_Complete, @@ -1552,6 +1555,7 @@ ASTConsumer &getASTConsumer() const { return Consumer; } ASTMutationListener *getASTMutationListener() const; ExternalSemaSource* getExternalSource() const { return ExternalSource; } + DarwinSDKInfo *getDarwinSDKInfo(); ///Registers an external source. If an external source already exists, /// creates a multiplex external source and appends to it. diff --git a/clang/lib/Basic/DarwinSDKInfo.cpp b/clang/lib/Basic/DarwinSDKInfo.cpp --- a/clang/lib/Basic/DarwinSDKInfo.cpp +++ b/clang/lib/Basic/DarwinSDKInfo.cpp @@ -14,6 +14,90 @@ using namespace clang; +Optional DarwinSDKInfo::RelatedTargetVersionMapping::map( + const VersionTuple &Key, const VersionTuple &MinimumValue, + const Optional &MaximumValue) const { + if (Key < MinimumKeyVersion) + return MinimumValue; + if (Key > MaximumKeyVersion) + return MaximumValue; + auto KV = Mapping.find(Key.normalize()); + if (KV != Mapping.end()) + return KV->getSecond(); + // If no exact entry found, try just the major key version. + if (Key.getMinor()) + return map(VersionTuple(Key.getMajor()), MinimumValue, MaximumValue); + // If this a major only key, return None for a missing entry. + return None; +} + +Optional +DarwinSDKInfo::RelatedTargetVersionMapping::parseJSON( + const llvm::json::Object &Obj, VersionTuple MaximumDeploymentTarget) { + VersionTuple Min = VersionTuple(std::numeric_limits::max()); + VersionTuple Max = VersionTuple(0); + VersionTuple MinValue = Min; + llvm::DenseMap Mapping; + for (const auto &KV : Obj) { + if (auto Val = KV.getSecond().getAsString()) { + llvm::VersionTuple KeyVersion; + llvm::VersionTuple ValueVersion; + if (KeyVersion.tryParse(KV.getFirst()) || ValueVersion.tryParse(*Val)) + return None; + Mapping[KeyVersion.normalize()] = ValueVersion; + if (KeyVersion < Min) + Min = KeyVersion; + if (KeyVersion > Max) + Max = KeyVersion; + if (ValueVersion < MinValue) + MinValue = ValueVersion; + } + } + if (Mapping.empty()) + return None; + return RelatedTargetVersionMapping( + Min, Max, MinValue, MaximumDeploymentTarget, std::move(Mapping)); +} + +static Optional getVersionKey(const llvm::json::Object &Obj, + StringRef Key) { + auto Value = Obj.getString(Key); + if (!Value) + return None; + VersionTuple Version; + if (Version.tryParse(*Value)) + return None; + return Version; +} + +Optional +DarwinSDKInfo::parseDarwinSDKSettingsJSON(StringRef FilePath, + const llvm::json::Object *Obj) { + auto Version = getVersionKey(*Obj, "Version"); + if (!Version) + return None; + auto MaximumDeploymentVersion = + getVersionKey(*Obj, "MaximumDeploymentTarget"); + if (!MaximumDeploymentVersion) + return None; + llvm::DenseMap> + VersionMappings; + if (const auto *VM = Obj->getObject("VersionMap")) { + if (const auto *Mapping = VM->getObject("macOS_iOSMac")) { + auto VersionMap = RelatedTargetVersionMapping::parseJSON( + *Mapping, *MaximumDeploymentVersion); + if (!VersionMap) + return None; + VersionMappings[OSEnvPair::macOStoMacCatalystPair().Value] = + std::move(VersionMap); + } + } + + return DarwinSDKInfo(std::move(*Version), + std::move(*MaximumDeploymentVersion), + std::move(VersionMappings)); +} + Expected> clang::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) { llvm::SmallString<256> Filepath = SDKRootPath; @@ -30,12 +114,8 @@ return Result.takeError(); if (const auto *Obj = Result->getAsObject()) { - auto VersionString = Obj->getString("Version"); - if (VersionString) { - VersionTuple Version; - if (!Version.tryParse(*VersionString)) - return DarwinSDKInfo(Version); - } + if (auto SDKInfo = DarwinSDKInfo::parseDarwinSDKSettingsJSON(Filepath, Obj)) + return std::move(SDKInfo); } return llvm::make_error("invalid SDKSettings.json", llvm::inconvertibleErrorCode()); diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -1506,7 +1506,9 @@ bool IsValid = !Version.tryParse(OSVersion); (void)IsValid; assert(IsValid && "invalid SDK version"); - return DarwinSDKInfo(Version); + return DarwinSDKInfo( + Version, + /*MaximumDeploymentTarget=*/VersionTuple(Version.getMajor(), 0, 99)); } private: diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -22,12 +22,14 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyDeclStackTrace.h" #include "clang/AST/StmtCXX.h" +#include "clang/Basic/DarwinSDKInfo.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Stack.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/CXXFieldCollector.h" #include "clang/Sema/DelayedDiagnostic.h" @@ -55,6 +57,29 @@ ModuleLoader &Sema::getModuleLoader() const { return PP.getModuleLoader(); } +DarwinSDKInfo *Sema::getDarwinSDKInfo() { + if (!CachedDarwinSDKInfo) { + auto SDKInfo = parseDarwinSDKInfo( + PP.getFileManager().getVirtualFileSystem(), + PP.getHeaderSearchInfo().getHeaderSearchOpts().Sysroot); + if (SDKInfo && *SDKInfo) + CachedDarwinSDKInfo = + std::make_unique(std::move(**SDKInfo)); + else { + if (!SDKInfo) + llvm::handleAllErrors(SDKInfo.takeError(), + [](const llvm::ErrorInfoBase &) { + // Ignore the error and pretend the SDK info + // is missing. + // FIXME: should we care about missing SDK info + // for some darwin targets? + }); + CachedDarwinSDKInfo = std::unique_ptr(); + } + } + return CachedDarwinSDKInfo->get(); +} + IdentifierInfo * Sema::InventAbbreviatedTemplateParameterTypeName(IdentifierInfo *ParamName, unsigned int Index) { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -23,6 +23,7 @@ #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/DarwinSDKInfo.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetBuiltins.h" @@ -2561,32 +2562,74 @@ S.Context.getTargetInfo().getTriple().isMacCatalystEnvironment()) { // Transcribe "ios" to "maccatalyst" (and add a new attribute). IdentifierInfo *NewII = nullptr; - auto MinIOSMacVersion = [](const VersionTuple &V) -> VersionTuple { - if (V.empty()) - return V; - if (V.getMajor() < 13 || - (V.getMajor() == 13 && V.getMinor() && *V.getMinor() < 1)) - return VersionTuple(13, 1); // The minimum Mac Catalyst version is 13.1. - return V; - }; - llvm::function_ref VersionRemapper = - MinIOSMacVersion; if (II->getName() == "ios") NewII = &S.Context.Idents.get("maccatalyst"); else if (II->getName() == "ios_app_extension") NewII = &S.Context.Idents.get("maccatalyst_app_extension"); - // FIXME: Add support for transcribing macOS availability using mapping from - // SDKSettings.json. if (NewII) { + auto MinIOSMacVersion = [](const VersionTuple &V) -> VersionTuple { + if (V.empty()) + return V; + if (V.getMajor() < 13 || + (V.getMajor() == 13 && V.getMinor() && *V.getMinor() < 1)) + return VersionTuple(13, + 1); // The minimum Mac Catalyst version is 13.1. + return V; + }; AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( ND, AL.getRange(), NewII, true /*Implicit*/, - VersionRemapper(Introduced.Version), - VersionRemapper(Deprecated.Version), - VersionRemapper(Obsoleted.Version), IsUnavailable, Str, IsStrict, + MinIOSMacVersion(Introduced.Version), + MinIOSMacVersion(Deprecated.Version), + MinIOSMacVersion(Obsoleted.Version), IsUnavailable, Str, IsStrict, Replacement, Sema::AMK_None, PriorityModifier + Sema::AP_InferredFromOtherPlatform); if (NewAttr) D->addAttr(NewAttr); + } else if (II->getName() == "macos" && S.getDarwinSDKInfo() && + (!Introduced.Version.empty() || !Deprecated.Version.empty() || + !Obsoleted.Version.empty())) { + if (const auto *MacOStoMacCatalystMapping = + S.getDarwinSDKInfo()->getVersionMapping( + DarwinSDKInfo::OSEnvPair::macOStoMacCatalystPair())) { + // Infer Mac Catalyst availability from the macOS availability attribute + // if it has versioned availability. Don't infer 'unavailable'. + // This inferred availability has + // lower priority than the other availability attributes that are + // inferred from 'ios'. + NewII = &S.Context.Idents.get("maccatalyst"); + auto RemapMacOSVersion = + [&](const VersionTuple &V) -> Optional { + if (V.empty()) + return None; + // API_TO_BE_DEPRECATED is 100000. + if (V.getMajor() == 100000) + return VersionTuple(100000); + // The minimum iosmac version is 13.1 + return MacOStoMacCatalystMapping->map(V, VersionTuple(13, 1), None); + }; + Optional NewIntroduced = + RemapMacOSVersion(Introduced.Version), + NewDeprecated = + RemapMacOSVersion(Deprecated.Version), + NewObsoleted = + RemapMacOSVersion(Obsoleted.Version); + if (NewIntroduced || NewDeprecated || NewObsoleted) { + auto VersionOrEmptyVersion = + [](const Optional &V) -> VersionTuple { + return V ? *V : VersionTuple(); + }; + AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( + ND, AL.getRange(), NewII, true /*Implicit*/, + VersionOrEmptyVersion(NewIntroduced), + VersionOrEmptyVersion(NewDeprecated), + VersionOrEmptyVersion(NewObsoleted), /*IsUnavailable=*/false, Str, + IsStrict, Replacement, Sema::AMK_None, + PriorityModifier + Sema::AP_InferredFromOtherPlatform + + Sema::AP_InferredFromOtherPlatform); + if (NewAttr) + D->addAttr(NewAttr); + } + } } } } diff --git a/clang/test/Driver/Inputs/MacOSX10.14.sdk/SDKSettings.json b/clang/test/Driver/Inputs/MacOSX10.14.sdk/SDKSettings.json --- a/clang/test/Driver/Inputs/MacOSX10.14.sdk/SDKSettings.json +++ b/clang/test/Driver/Inputs/MacOSX10.14.sdk/SDKSettings.json @@ -1 +1 @@ -{"Version":"10.14"} +{"Version":"10.14", "MaximumDeploymentTarget": "10.14.99"} diff --git a/clang/test/Driver/Inputs/WatchOS6.0.sdk/SDKSettings.json b/clang/test/Driver/Inputs/WatchOS6.0.sdk/SDKSettings.json --- a/clang/test/Driver/Inputs/WatchOS6.0.sdk/SDKSettings.json +++ b/clang/test/Driver/Inputs/WatchOS6.0.sdk/SDKSettings.json @@ -1 +1 @@ -{"Version":"6.0.0"} +{"Version":"6.0.0", "MaximumDeploymentTarget": "6.0.99"} diff --git a/clang/test/Driver/Inputs/iPhoneOS13.0.sdk/SDKSettings.json b/clang/test/Driver/Inputs/iPhoneOS13.0.sdk/SDKSettings.json --- a/clang/test/Driver/Inputs/iPhoneOS13.0.sdk/SDKSettings.json +++ b/clang/test/Driver/Inputs/iPhoneOS13.0.sdk/SDKSettings.json @@ -1 +1 @@ -{"Version":"13.0"} +{"Version":"13.0", "MaximumDeploymentTarget": "13.0.99"} diff --git a/clang/test/Sema/Inputs/MacOSX11.0.sdk/SDKSettings.json b/clang/test/Sema/Inputs/MacOSX11.0.sdk/SDKSettings.json new file mode 100644 --- /dev/null +++ b/clang/test/Sema/Inputs/MacOSX11.0.sdk/SDKSettings.json @@ -0,0 +1,23 @@ +{ + "DefaultVariant": "macos", "DisplayName": "macOS 11", + "Version": "11.0", + "MaximumDeploymentTarget": "11.0.99", + "PropertyConditionFallbackNames": [], "VersionMap": { + "iOSMac_macOS": { + "13.2": "10.15.1", + "13.4": "10.15.4", + "13.3.1": "10.15.3", + "13.3": "10.15.2", + "13.1": "10.15", + "14.0": "11.0" + }, + "macOS_iOSMac": { + "10.15.2": "13.3", + "11.0": "14.0", + "10.15": "13.1", + "10.15.3": "13.3.1", + "10.15.1": "13.2", + "10.15.4": "13.4" + } + } +} diff --git a/clang/test/Sema/attr-availability-iosmac-infer-from-macos.c b/clang/test/Sema/attr-availability-iosmac-infer-from-macos.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-availability-iosmac-infer-from-macos.c @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 "-triple" "x86_64-apple-ios13.1-macabi" -isysroot %S/Inputs/MacOSX11.0.sdk -fsyntax-only -verify %s +// RUN: %clang_cc1 "-triple" "x86_64-apple-ios14-macabi" -isysroot %S/Inputs/MacOSX11.0.sdk -DIOS14 -fsyntax-only -verify %s + +void f0(void) __attribute__((availability(macOS, introduced = 10.11))); +void f1(void) __attribute__((availability(macOS, introduced = 10.15))); +void f2(void) __attribute__(( // expected-note {{'f2' has been explicitly marked deprecated here}} + availability(macOS, introduced = 10.11, + deprecated = 10.12))); +void f3(void) + __attribute__((availability(macOS, introduced = 10.11, deprecated = 10.14))) + __attribute__((availability(iOS, introduced = 11.0))); + +void f4(void) +__attribute__((availability(macOS, introduced = 10, deprecated = 100000))); + +void fAvail() __attribute__((availability(macOS, unavailable))); + +void f16() __attribute__((availability(macOS, introduced = 11.0))); +#ifndef IOS14 +// expected-note@-2 {{here}} +#endif + +void fObs() __attribute__((availability(macOS, introduced = 10.11, obsoleted = 10.15))); // expected-note {{'fObs' has been explicitly marked unavailable here}} + +void fAPItoDepr() __attribute__((availability(macOS, introduced = 10.11, deprecated = 100000))); + +void dontRemapFutureVers() __attribute__((availability(macOS, introduced = 20))); + +void usage() { + f0(); + f1(); + f2(); // expected-warning {{'f2' is deprecated: first deprecated in macCatalyst 13.1}} + f3(); + f4(); + fAvail(); + f16(); +#ifndef IOS14 + // expected-warning@-2 {{'f16' is only available on macCatalyst 14.0 or newer}} expected-note@-2 {{enclose}} +#endif + fObs(); // expected-error {{'fObs' is unavailable: obsoleted in macCatalyst 13.1}} + fAPItoDepr(); + dontRemapFutureVers(); +} + +#ifdef IOS14 + +void f15_4(void) __attribute__((availability(macOS, introduced = 10.15, deprecated = 10.15.4))); // expected-note {{here}} +void f15_3(void) __attribute__((availability(macOS, introduced = 10.15, deprecated = 10.15.3))); // expected-note {{here}} +void f15_2(void) __attribute__((availability(macOS, introduced = 10.15, deprecated = 10.15.2))); // expected-note {{here}} + +void usage16() { + f15_2(); // expected-warning {{'f15_2' is deprecated: first deprecated in macCatalyst 13.3}} + f15_3(); // expected-warning {{'f15_3' is deprecated: first deprecated in macCatalyst 13.3.1}} + f15_4(); // expected-warning {{'f15_4' is deprecated: first deprecated in macCatalyst 13.4}} + f16(); +} + +#endif diff --git a/llvm/include/llvm/Support/VersionTuple.h b/llvm/include/llvm/Support/VersionTuple.h --- a/llvm/include/llvm/Support/VersionTuple.h +++ b/llvm/include/llvm/Support/VersionTuple.h @@ -14,6 +14,7 @@ #ifndef LLVM_SUPPORT_VERSIONTUPLE_H #define LLVM_SUPPORT_VERSIONTUPLE_H +#include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/Optional.h" #include @@ -95,6 +96,20 @@ return *this; } + /// Return a version tuple that contains only components that are non-zero. + VersionTuple normalize() const { + VersionTuple Result = *this; + if (Result.Build == 0) { + Result.HasBuild = false; + if (Result.Subminor == 0) { + Result.HasSubminor = false; + if (Result.Minor == 0) + Result.HasMinor = false; + } + } + return Result; + } + /// 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) { @@ -161,5 +176,28 @@ /// Print a version number. raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V); +// Provide DenseMapInfo for version tuples. +template <> struct DenseMapInfo { + static inline VersionTuple getEmptyKey() { return VersionTuple(0x7FFFFFFF); } + static inline VersionTuple getTombstoneKey() { + return VersionTuple(0x7FFFFFFE); + } + static unsigned getHashValue(const VersionTuple &value) { + unsigned result = value.getMajor(); + if (auto minor = value.getMinor()) + result = detail::combineHashValue(result, *minor); + if (auto subminor = value.getSubminor()) + result = detail::combineHashValue(result, *subminor); + if (auto build = value.getBuild()) + result = detail::combineHashValue(result, *build); + + return result; + } + + static bool isEqual(const VersionTuple &lhs, const VersionTuple &rhs) { + return lhs == rhs; + } +}; + } // end namespace llvm #endif // LLVM_SUPPORT_VERSIONTUPLE_H