Files
swift-mirror/lib/Basic/Version.cpp
swift-ci 645d73f27e Merge pull request #58480 from beccadax/so-i-put-versions-in-your-version-numbers
Update SWIFT_COMPILER_VERSION language features
2022-05-24 17:47:43 -07:00

512 lines
16 KiB
C++

//===--- Version.cpp - Swift Version Number -------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines several version-related utility functions for Swift.
//
//===----------------------------------------------------------------------===//
#include "clang/Basic/CharInfo.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/Version.h"
#include <vector>
#define TOSTR2(X) #X
#define TOSTR(X) TOSTR2(X)
#ifdef SWIFT_VERSION_PATCHLEVEL
/// Helper macro for SWIFT_VERSION_STRING.
#define SWIFT_MAKE_VERSION_STRING(X, Y, Z) TOSTR(X) "." TOSTR(Y) "." TOSTR(Z)
/// A string that describes the Swift version number, e.g., "1.0".
#define SWIFT_VERSION_STRING \
SWIFT_MAKE_VERSION_STRING(SWIFT_VERSION_MAJOR, SWIFT_VERSION_MINOR, \
SWIFT_VERSION_PATCHLEVEL)
#else
/// Helper macro for SWIFT_VERSION_STRING.
#define SWIFT_MAKE_VERSION_STRING(X, Y) TOSTR(X) "." TOSTR(Y)
/// A string that describes the Swift version number, e.g., "1.0".
#define SWIFT_VERSION_STRING \
SWIFT_MAKE_VERSION_STRING(SWIFT_VERSION_MAJOR, SWIFT_VERSION_MINOR)
#endif
#include "LLVMRevision.inc"
#include "SwiftRevision.inc"
namespace swift {
namespace version {
/// Print a string of the form "LLVM xxxxx, Swift zzzzz", where each placeholder
/// is the revision for the associated repository.
static void printFullRevisionString(raw_ostream &out) {
// Arbitrarily truncate to 15 characters. This should be enough to unique Git
// hashes while keeping the REPL version string from overflowing 80 columns.
#if defined(LLVM_REVISION)
out << "LLVM " << StringRef(LLVM_REVISION).slice(0, 15);
# if defined(SWIFT_REVISION)
out << ", ";
# endif
#endif
#if defined(SWIFT_REVISION)
out << "Swift " << StringRef(SWIFT_REVISION).slice(0, 15);
#endif
}
static void splitVersionComponents(
SmallVectorImpl<std::pair<StringRef, SourceRange>> &SplitComponents,
StringRef &VersionString, SourceLoc Loc,
bool skipQuote = false) {
SourceLoc Start = (Loc.isValid() && skipQuote) ? Loc.getAdvancedLoc(1) : Loc;
SourceLoc End = Start;
// Split the version string into tokens separated by the '.' character.
while (!VersionString.empty()) {
StringRef SplitComponent, Rest;
std::tie(SplitComponent, Rest) = VersionString.split('.');
if (Loc.isValid()) {
End = End.getAdvancedLoc(SplitComponent.size());
}
auto Range = Loc.isValid() ? SourceRange(Start, End) : SourceRange();
if (Loc.isValid())
End = End.getAdvancedLoc(1);
Start = End;
SplitComponents.push_back({SplitComponent, Range});
VersionString = Rest;
}
}
Optional<Version> Version::parseCompilerVersionString(
StringRef VersionString, SourceLoc Loc, DiagnosticEngine *Diags) {
Version CV;
SmallString<16> digits;
llvm::raw_svector_ostream OS(digits);
SmallVector<std::pair<StringRef, SourceRange>, 5> SplitComponents;
splitVersionComponents(SplitComponents, VersionString, Loc,
/*skipQuote=*/true);
uint64_t ComponentNumber;
bool isValidVersion = true;
auto checkVersionComponent = [&](unsigned Component, SourceRange Range) {
unsigned limit = CV.Components.empty() ? 9223371 : 999;
if (Component > limit) {
if (Diags)
Diags->diagnose(Range.Start,
diag::compiler_version_component_out_of_range, limit);
isValidVersion = false;
}
};
for (size_t i = 0; i < SplitComponents.size(); ++i) {
StringRef SplitComponent;
SourceRange Range;
std::tie(SplitComponent, Range) = SplitComponents[i];
// Version components can't be empty.
if (SplitComponent.empty()) {
if (Diags)
Diags->diagnose(Range.Start, diag::empty_version_component);
isValidVersion = false;
continue;
}
// The second version component isn't used for comparison.
if (i == 1) {
if (!SplitComponent.equals("*")) {
if (Diags) {
// Majors 600-1300 were used for Swift 1.0-5.5 (based on clang
// versions), but then we reset the numbering based on Swift versions,
// so 5.6 had major 5. We assume that majors below 600 use the new
// scheme and equal/above it use the old scheme.
bool firstComponentLooksNew = CV.Components[0] < 600;
auto diag = Diags->diagnose(Range.Start,
diag::unused_compiler_version_component,
firstComponentLooksNew);
if (firstComponentLooksNew &&
!SplitComponent.getAsInteger(10, ComponentNumber)) {
// Fix-it version like "5.7.1.2.3" to "5007.*.1.2.3".
auto newDigits = llvm::formatv("{0}{1,0+3}.*", CV.Components[0],
ComponentNumber).str();
diag.fixItReplaceChars(SplitComponents[0].second.Start,
Range.End, newDigits);
}
else {
diag.fixItReplaceChars(Range.Start, Range.End, "*");
}
}
}
CV.Components.push_back(0);
continue;
}
// All other version components must be numbers.
if (!SplitComponent.getAsInteger(10, ComponentNumber)) {
checkVersionComponent(ComponentNumber, Range);
CV.Components.push_back(ComponentNumber);
continue;
} else {
if (Diags)
Diags->diagnose(Range.Start, diag::version_component_not_number);
isValidVersion = false;
}
}
if (CV.Components.size() > 5) {
if (Diags)
Diags->diagnose(Loc, diag::compiler_version_too_many_components);
isValidVersion = false;
}
// In the beginning, '_compiler_version(string-literal)' was designed for a
// different version scheme where the major was fairly large and the minor
// was ignored; now we use one where the minor is significant and major and
// minor match the Swift language version. See the comment above on
// `firstComponentLooksNew` for details.
//
// However, we want the string literal variant of '_compiler_version' to
// maintain source compatibility with old checks; that means checks for new
// versions have to be written so that old compilers will think they represent
// newer versions, while new compilers have to interpret old version number
// strings in a way that will compare correctly to the new versions compiled
// into them.
//
// To achieve this, modern compilers divide the major by 1000 and overwrite
// the wildcard component with the remainder, effectively shifting the last
// three digits of the major into the minor, before comparing it to the
// compiler version:
//
// _compiler_version("5007.*.1.2.3") -> 5.7.1.2.3
// _compiler_version("1300.*.1.2.3") -> 1.300.1.2.3 (smaller than 5.6)
// _compiler_version( "600.*.1.2.3") -> 0.600.1.2.3 (smaller than 5.6)
//
// So if you want to specify a 5.7.z.a.b version, we ask users to either write
// it as 5007.*.z.a.b, or to use the new '_compiler_version(>= version)'
// syntax instead, which does not perform this conversion.
if (!CV.Components.empty()) {
if (CV.Components.size() == 1)
CV.Components.push_back(0);
CV.Components[1] = CV.Components[0] % 1000;
CV.Components[0] = CV.Components[0] / 1000;
}
return isValidVersion ? Optional<Version>(CV) : None;
}
Optional<Version> Version::parseVersionString(StringRef VersionString,
SourceLoc Loc,
DiagnosticEngine *Diags) {
Version TheVersion;
SmallString<16> digits;
llvm::raw_svector_ostream OS(digits);
SmallVector<std::pair<StringRef, SourceRange>, 5> SplitComponents;
// Skip over quote character in string literal.
if (VersionString.empty()) {
if (Diags)
Diags->diagnose(Loc, diag::empty_version_string);
return None;
}
splitVersionComponents(SplitComponents, VersionString, Loc, Diags);
uint64_t ComponentNumber;
bool isValidVersion = true;
for (size_t i = 0; i < SplitComponents.size(); ++i) {
StringRef SplitComponent;
SourceRange Range;
std::tie(SplitComponent, Range) = SplitComponents[i];
// Version components can't be empty.
if (SplitComponent.empty()) {
if (Diags)
Diags->diagnose(Range.Start, diag::empty_version_component);
isValidVersion = false;
continue;
}
// All other version components must be numbers.
if (!SplitComponent.getAsInteger(10, ComponentNumber)) {
TheVersion.Components.push_back(ComponentNumber);
continue;
} else {
if (Diags)
Diags->diagnose(Range.Start,
diag::version_component_not_number);
isValidVersion = false;
}
}
return isValidVersion ? Optional<Version>(TheVersion) : None;
}
Version::Version(StringRef VersionString,
SourceLoc Loc,
DiagnosticEngine *Diags)
: Version(*parseVersionString(VersionString, Loc, Diags))
{}
Version Version::getCurrentCompilerVersion() {
#ifdef SWIFT_COMPILER_VERSION
auto currentVersion = Version::parseVersionString(
SWIFT_COMPILER_VERSION, SourceLoc(), nullptr);
assert(currentVersion.hasValue() &&
"Embedded Swift language version couldn't be parsed: '"
SWIFT_COMPILER_VERSION
"'");
return currentVersion.getValue();
#else
return Version();
#endif
}
Version Version::getCurrentLanguageVersion() {
#if SWIFT_VERSION_PATCHLEVEL
return {SWIFT_VERSION_MAJOR, SWIFT_VERSION_MINOR, SWIFT_VERSION_PATCHLEVEL};
#else
return {SWIFT_VERSION_MAJOR, SWIFT_VERSION_MINOR};
#endif
}
raw_ostream &operator<<(raw_ostream &os, const Version &version) {
if (version.empty())
return os;
os << version[0];
for (size_t i = 1, e = version.size(); i != e; ++i)
os << '.' << version[i];
return os;
}
std::string
Version::preprocessorDefinition(StringRef macroName,
ArrayRef<uint64_t> componentWeights) const {
uint64_t versionConstant = 0;
for (size_t i = 0, e = std::min(componentWeights.size(), Components.size());
i < e; ++i) {
versionConstant += componentWeights[i] * Components[i];
}
std::string define("-D");
llvm::raw_string_ostream(define) << macroName << '=' << versionConstant;
// This isn't using stream.str() so that we get move semantics.
return define;
}
Version::operator llvm::VersionTuple() const
{
switch (Components.size()) {
case 0:
return llvm::VersionTuple();
case 1:
return llvm::VersionTuple((unsigned)Components[0]);
case 2:
return llvm::VersionTuple((unsigned)Components[0],
(unsigned)Components[1]);
case 3:
return llvm::VersionTuple((unsigned)Components[0],
(unsigned)Components[1],
(unsigned)Components[2]);
case 4:
case 5:
return llvm::VersionTuple((unsigned)Components[0],
(unsigned)Components[1],
(unsigned)Components[2],
(unsigned)Components[3]);
default:
llvm_unreachable("swift::version::Version with 6 or more components");
}
}
Optional<Version> Version::getEffectiveLanguageVersion() const {
switch (size()) {
case 0:
return None;
case 1:
break;
case 2:
// The only valid explicit language version with a minor
// component is 4.2.
if (Components[0] == 4 && Components[1] == 2)
break;
return None;
default:
// We do not want to permit users requesting more precise effective language
// versions since accepting such an argument promises more than we're able
// to deliver.
return None;
}
// FIXME: When we switch to Swift 5 by default, the "4" case should return
// a version newer than any released 4.x compiler, and the
// "5" case should start returning getCurrentLanguageVersion. We should
// also check for the presence of SWIFT_VERSION_PATCHLEVEL, and if that's
// set apply it to the "3" case, so that Swift 4.0.1 will automatically
// have a compatibility mode of 3.2.1.
switch (Components[0]) {
case 4:
// Version '4' on its own implies '4.1.50'.
if (size() == 1)
return Version{4, 1, 50};
// This should be true because of the check up above.
assert(size() == 2 && Components[0] == 4 && Components[1] == 2);
return Version{4, 2};
case 5:
static_assert(SWIFT_VERSION_MAJOR == 5,
"getCurrentLanguageVersion is no longer correct here");
return Version::getCurrentLanguageVersion();
case 6:
// Allow version '6' in asserts compilers *only* so that we can start
// testing changes slated for Swift 6. Note that it's still not listed in
// `Version::getValidEffectiveVersions()`.
// FIXME: When Swift 6 becomes real, remove 'REQUIRES: asserts' from tests
// using '-swift-version 6'.
#ifdef NDEBUG
LLVM_FALLTHROUGH;
#else
return Version{6};
#endif
default:
return None;
}
}
Version Version::asMajorVersion() const {
if (empty())
return {};
Version res;
res.Components.push_back(Components[0]);
return res;
}
std::string Version::asAPINotesVersionString() const {
// Other than for "4.2.x", map the Swift major version into
// the API notes version for Swift. This has the effect of allowing
// API notes to effect changes only on Swift major versions,
// not minor versions.
if (size() >= 2 && Components[0] == 4 && Components[1] == 2)
return "4.2";
return llvm::itostr(Components[0]);
}
bool operator>=(const class Version &lhs,
const class Version &rhs) {
// The empty compiler version represents the latest possible version,
// usually built from the source repository.
if (lhs.empty())
return true;
auto n = std::max(lhs.size(), rhs.size());
for (size_t i = 0; i < n; ++i) {
auto lv = i < lhs.size() ? lhs[i] : 0;
auto rv = i < rhs.size() ? rhs[i] : 0;
if (lv < rv)
return false;
else if (lv > rv)
return true;
}
// Equality
return true;
}
bool operator<(const class Version &lhs, const class Version &rhs) {
return !(lhs >= rhs);
}
bool operator==(const class Version &lhs,
const class Version &rhs) {
auto n = std::max(lhs.size(), rhs.size());
for (size_t i = 0; i < n; ++i) {
auto lv = i < lhs.size() ? lhs[i] : 0;
auto rv = i < rhs.size() ? rhs[i] : 0;
if (lv != rv)
return false;
}
return true;
}
std::pair<unsigned, unsigned> getSwiftNumericVersion() {
return { SWIFT_VERSION_MAJOR, SWIFT_VERSION_MINOR };
}
std::string getSwiftFullVersion(Version effectiveVersion) {
std::string buf;
llvm::raw_string_ostream OS(buf);
#ifdef SWIFT_VENDOR
OS << SWIFT_VENDOR " ";
#endif
OS << "Swift version " SWIFT_VERSION_STRING;
#ifndef SWIFT_COMPILER_VERSION
OS << "-dev";
#endif
if (effectiveVersion != Version::getCurrentLanguageVersion()) {
OS << " effective-" << effectiveVersion;
}
#if defined(SWIFT_COMPILER_VERSION)
OS << " (swiftlang-" SWIFT_COMPILER_VERSION;
#if defined(CLANG_COMPILER_VERSION)
OS << " clang-" CLANG_COMPILER_VERSION;
#endif
OS << ")";
#elif defined(LLVM_REVISION) || defined(SWIFT_REVISION)
OS << " (";
printFullRevisionString(OS);
OS << ")";
#endif
// Suppress unused function warning
(void)&printFullRevisionString;
return OS.str();
}
StringRef getSwiftRevision() {
#ifdef SWIFT_REVISION
return SWIFT_REVISION;
#else
return "";
#endif
}
bool isCurrentCompilerTagged() {
#ifdef SWIFT_COMPILER_VERSION
return true;
#else
return false;
#endif
}
} // end namespace version
} // end namespace swift