Files
swift-mirror/lib/Basic/Version.cpp
David Farler c32fb8e7b9 SE-0020: Swift Language Version Build Configuration
Introduce a new "swift" build configuration that guards declarations
and statements with a language version - if the current language version
of the compiler is at least that version, the block will parse as normal.
For inactive blocks, the code will not be parsed an no diagnostics will
be emitted there.

Example:

    #if swift(>=2.2)
      print("Active")
    #else
      this code will not parse or emit diagnostics
    #endif

https://github.com/apple/swift-evolution/blob/master/proposals/0020-if-swift-version.md
rdar://problem/19823607
2016-01-21 16:31:19 -08:00

349 lines
10 KiB
C++

//===--- Version.cpp - Swift Version Number -------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://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/ADT/SmallString.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
#if __has_include("LLVMRevision.inc")
# include "LLVMRevision.inc"
#endif
#if __has_include("ClangRevision.inc")
# include "ClangRevision.inc"
#endif
#if __has_include("SwiftRevision.inc")
# include "SwiftRevision.inc"
#endif
namespace swift {
namespace version {
/// Print a string of the form "LLVM xxxxx, Clang yyyyy, Swift zzzzz",
/// where each placeholder is the revision for the associated repository.
static void printFullRevisionString(raw_ostream &out) {
// Arbitrarily truncate to 10 characters. This should be enough to unique
// Git hashes for the time being, and certainly enough for SVN revisions,
// while keeping the version string from being ridiculously long.
#if defined(LLVM_REVISION)
out << "LLVM " << StringRef(LLVM_REVISION).slice(0, 10);
# if defined(CLANG_REVISION) || defined(SWIFT_REVISION)
out << ", ";
# endif
#endif
#if defined(CLANG_REVISION)
out << "Clang " << StringRef(CLANG_REVISION).slice(0, 10);
# if defined(SWIFT_REVISION)
out << ", ";
# endif
#endif
#if defined(SWIFT_REVISION)
out << "Swift " << StringRef(SWIFT_REVISION).slice(0, 10);
#endif
}
void splitVersionComponents(
SmallVectorImpl<std::pair<StringRef, SourceRange>> &SplitComponents,
StringRef &VersionString, SourceLoc Loc,
DiagnosticEngine *Diags, 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;
}
}
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;
auto checkVersionComponent = [&](unsigned Component, SourceRange Range) {
unsigned limit = CV.Components.size() == 0 ? 9223371 : 999;
if (Component > limit) {
if (Diags)
Diags->diagnose(Range.Start,
diag::compiler_version_component_out_of_range, limit);
else
llvm_unreachable("Compiler version component out of range");
}
};
splitVersionComponents(SplitComponents, VersionString, Loc, Diags,
/*skipQuote=*/true);
uint64_t ComponentNumber;
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);
else
llvm_unreachable("Found empty compiler version component");
continue;
}
// The second version component isn't used for comparison.
if (i == 1) {
if (!SplitComponent.equals("*")) {
if (Diags) {
Diags->diagnose(Range.Start, diag::unused_compiler_version_component)
.fixItReplaceChars(Range.Start, Range.End, "*");
} else {
llvm_unreachable("Expected * for second compiler version component");
}
}
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);
} else {
llvm_unreachable("Invalid character in _compiler_version build configuration");
}
}
if (CV.Components.size() > 5) {
if (Diags) {
Diags->diagnose(Loc, diag::compiler_version_too_many_components);
} else {
llvm_unreachable("Compiler version must not have more than 5 components");
}
}
return CV;
}
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::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;
#else
return Version();
#endif
}
Version Version::getCurrentLanguageVersion() {
#ifndef SWIFT_VERSION_STRING
#error Swift language version is not set!
#endif
auto currentVersion = Version::parseVersionString(
SWIFT_VERSION_STRING, SourceLoc(), nullptr);
assert(currentVersion.hasValue() &&
"Embedded Swift language version couldn't be parsed: '"
SWIFT_VERSION_STRING
"'");
return currentVersion.getValue();
}
std::string Version::str() const {
std::string VersionString;
llvm::raw_string_ostream OS(VersionString);
for (auto i = Components.begin(); i != Components.end(); i++) {
OS << *i;
if (i != Components.end() - 1)
OS << '.';
}
return OS.str();
}
std::string Version::preprocessorDefinition() const {
SmallString<64> define("-D__SWIFT_COMPILER_VERSION=");
llvm::raw_svector_ostream OS(define);
uint64_t versionConstant = 0;
auto NumComponents = Components.size();
if (NumComponents > 0)
versionConstant += Components[0] * 1000 * 1000 * 1000;
// Component 2 is not used.
if (NumComponents > 2)
versionConstant += Components[2] * 1000 * 1000;
if (NumComponents > 3)
versionConstant += Components[3] * 1000;
if (NumComponents > 4)
versionConstant += Components[4];
OS << versionConstant;
return OS.str().str();
}
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::min(lhs.size(), rhs.size());
for (size_t i = 0; i < n; ++i) {
if (lhs[i] < rhs[i])
return false;
else if (lhs[i] > rhs[i])
return true;
}
return lhs.size() >= rhs.size();
}
std::pair<unsigned, unsigned> getSwiftNumericVersion() {
return { SWIFT_VERSION_MAJOR, SWIFT_VERSION_MINOR };
}
std::string getSwiftFullVersion() {
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 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(CLANG_REVISION) || \
defined(SWIFT_REVISION)
OS << " (";
printFullRevisionString(OS);
OS << ")";
#endif
return OS.str();
}
} // end namespace version
} // end namespace swift