"-swift-version 3" means Swift 3.1, not 3.0. (#7883)

Put in a general mechanism for mapping user-specified "compatibility
versions" to proper "effective versions" (what #if and @available
checking should respect). This may still be different from the
intrinsic "language version"; right now master is considered a "3.1"
compiler with a "Swift 4 mode", and we plan to ship a "4.0" compiler
with a "Swift 3 mode" that will have a version number of something
like "3.2".

rdar://problem/29884401 / SR-3791
This commit is contained in:
Jordan Rose
2017-03-03 13:28:01 -08:00
committed by GitHub
parent bc6ea5b00f
commit 3456d04925
7 changed files with 130 additions and 42 deletions

View File

@@ -52,13 +52,16 @@ namespace version {
/// a: [0 - 999] /// a: [0 - 999]
/// b: [0 - 999] /// b: [0 - 999]
class Version { class Version {
SmallVector<uint64_t, 5> Components; SmallVector<unsigned, 5> Components;
public: public:
/// Create the empty compiler version - this always compares greater /// Create the empty compiler version - this always compares greater
/// or equal to any other CompilerVersion, as in the case of building Swift /// or equal to any other CompilerVersion, as in the case of building Swift
/// from latest sources outside of a build/integration/release context. /// from latest sources outside of a build/integration/release context.
Version() = default; Version() = default;
/// Create a literal version from a list of components.
Version(std::initializer_list<unsigned> Values) : Components(Values) {}
/// Create a version from a string in source code. /// Create a version from a string in source code.
/// ///
/// Must include only groups of digits separated by a dot. /// Must include only groups of digits separated by a dot.
@@ -94,11 +97,15 @@ public:
/// away any 5th component that might be in this version. /// away any 5th component that might be in this version.
operator clang::VersionTuple() const; operator clang::VersionTuple() const;
/// Return whether this version is a valid Swift language version number /// Returns the concrete version to use when \e this version is provided as
/// to set the compiler to using -swift-version; this is not the same as /// an argument to -swift-version.
/// the set of Swift versions that have ever existed, just those that we ///
/// are attempting to maintain backward-compatibility support for. /// This is not the same as the set of Swift versions that have ever existed,
bool isValidEffectiveLanguageVersion() const; /// just those that we are attempting to maintain backward-compatibility
/// support for. It's also common for valid versions to produce a different
/// result; for example "-swift-version 3" at one point instructed the
/// compiler to act as if it is version 3.1.
Optional<Version> getEffectiveLanguageVersion() const;
/// Whether this version is in the Swift 3 family /// Whether this version is in the Swift 3 family
bool isVersion3() const { return !empty() && Components[0] == 3; } bool isVersion3() const { return !empty() && Components[0] == 3; }
@@ -141,6 +148,10 @@ bool operator==(const Version &lhs, const Version &rhs);
raw_ostream &operator<<(raw_ostream &os, const Version &version); raw_ostream &operator<<(raw_ostream &os, const Version &version);
/// Retrieves the numeric {major, minor} Swift version. /// Retrieves the numeric {major, minor} Swift version.
///
/// Note that this is the underlying version of the language, ignoring any
/// -swift-version flags that may have been used in a particular invocation of
/// the compiler.
std::pair<unsigned, unsigned> getSwiftNumericVersion(); std::pair<unsigned, unsigned> getSwiftNumericVersion();
/// Retrieves a string representing the complete Swift version, which includes /// Retrieves a string representing the complete Swift version, which includes

View File

@@ -242,16 +242,11 @@ Version Version::getCurrentCompilerVersion() {
} }
Version Version::getCurrentLanguageVersion() { Version Version::getCurrentLanguageVersion() {
#ifndef SWIFT_VERSION_STRING #if SWIFT_VERSION_PATCHLEVEL
#error Swift language version is not set! return {SWIFT_VERSION_MAJOR, SWIFT_VERSION_MINOR, SWIFT_VERSION_PATCHLEVEL};
#else
return {SWIFT_VERSION_MAJOR, SWIFT_VERSION_MINOR};
#endif #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();
} }
raw_ostream &operator<<(raw_ostream &os, const Version &version) { raw_ostream &operator<<(raw_ostream &os, const Version &version) {
@@ -304,18 +299,35 @@ Version::operator clang::VersionTuple() const
} }
} }
bool Version::isValidEffectiveLanguageVersion() const { Optional<Version> Version::getEffectiveLanguageVersion() const {
for (auto verStr : getValidEffectiveVersions()) { switch (size()) {
auto v = parseVersionString(verStr, SourceLoc(), nullptr); case 0:
assert(v.hasValue()); return None;
// In this case, use logical-equality _and_ precision-equality. We do not case 1:
// want to permit users requesting effective language versions more precise break;
// than our whitelist (eg. we permit 3 but not 3.0 or 3.0.0), since default:
// accepting such an argument promises more than we're able to deliver. // We do not want to permit users requesting more precise effective language
if (v == *this && v.getValue().size() == size()) // versions since accepting such an argument promises more than we're able
return true; // to deliver.
return None;
}
// FIXME: When we switch to Swift 4 by default, the "3" case should return
// a version newer than any released 3.x compiler (probably "3.2"), and the
// "4" 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 3:
static_assert(SWIFT_VERSION_MAJOR == 3,
"getCurrentLanguageVersion is no longer correct here");
return Version::getCurrentLanguageVersion();
case 4:
return Version{4, 0};
default:
return None;
} }
return false;
} }
Version Version::asMajorVersion() const { Version Version::asMajorVersion() const {

View File

@@ -802,7 +802,7 @@ static void diagnoseSwiftVersion(Optional<version::Version> &vers, Arg *verArg,
// Check for an unneeded minor version, otherwise just list valid versions // Check for an unneeded minor version, otherwise just list valid versions
if (vers.hasValue() && !vers.getValue().empty() && if (vers.hasValue() && !vers.getValue().empty() &&
vers.getValue().asMajorVersion().isValidEffectiveLanguageVersion()) { vers.getValue().asMajorVersion().getEffectiveLanguageVersion()) {
diags.diagnose(SourceLoc(), diag::note_swift_version_major, diags.diagnose(SourceLoc(), diag::note_swift_version_major,
vers.getValue()[0]); vers.getValue()[0]);
} else { } else {
@@ -822,12 +822,15 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
if (auto A = Args.getLastArg(OPT_swift_version)) { if (auto A = Args.getLastArg(OPT_swift_version)) {
auto vers = version::Version::parseVersionString( auto vers = version::Version::parseVersionString(
A->getValue(), SourceLoc(), &Diags); A->getValue(), SourceLoc(), &Diags);
if (vers.hasValue() && bool isValid = false;
vers.getValue().isValidEffectiveLanguageVersion()) { if (vers.hasValue()) {
Opts.EffectiveLanguageVersion = vers.getValue(); if (auto effectiveVers = vers.getValue().getEffectiveLanguageVersion()) {
} else { Opts.EffectiveLanguageVersion = effectiveVers.getValue();
diagnoseSwiftVersion(vers, A, Args, Diags); isValid = true;
}
} }
if (!isValid)
diagnoseSwiftVersion(vers, A, Args, Diags);
} }
Opts.AttachCommentsToDecls |= Args.hasArg(OPT_dump_api_path); Opts.AttachCommentsToDecls |= Args.hasArg(OPT_dump_api_path);

View File

@@ -11,8 +11,26 @@ asdf // expected-error {{use of unresolved identifier}}
jkl jkl
#endif #endif
#if swift(>=3.1)
asdf // expected-error {{use of unresolved identifier}}
#else
jkl
#endif
#if swift(>=4) #if swift(>=4)
aoeu aoeu
#else #else
htn // expected-error {{use of unresolved identifier}} htn // expected-error {{use of unresolved identifier}}
#endif #endif
#if swift(>=4.1)
aoeu
#else
htn // expected-error {{use of unresolved identifier}}
#endif
#if swift(>=5)
aoeu
#else
htn // expected-error {{use of unresolved identifier}}
#endif

View File

@@ -1,4 +1,3 @@
// RUN: %target-swiftc_driver -swift-version 3 %s
// RUN: not %target-swiftc_driver -swift-version foo %s 2>&1 | %FileCheck --check-prefix BAD %s // RUN: not %target-swiftc_driver -swift-version foo %s 2>&1 | %FileCheck --check-prefix BAD %s
// RUN: not %target-swiftc_driver -swift-version 1 %s 2>&1 | %FileCheck --check-prefix BAD %s // RUN: not %target-swiftc_driver -swift-version 1 %s 2>&1 | %FileCheck --check-prefix BAD %s
// RUN: not %target-swiftc_driver -swift-version 2 %s 2>&1 | %FileCheck --check-prefix BAD %s // RUN: not %target-swiftc_driver -swift-version 2 %s 2>&1 | %FileCheck --check-prefix BAD %s
@@ -9,10 +8,50 @@
// RUN: not %target-swiftc_driver -swift-version 3.3 %s 2>&1 | %FileCheck --check-prefix FIXIT_3 %s // RUN: not %target-swiftc_driver -swift-version 3.3 %s 2>&1 | %FileCheck --check-prefix FIXIT_3 %s
// RUN: not %target-swiftc_driver -swift-version 4.3 %s 2>&1 | %FileCheck --check-prefix FIXIT_4 %s // RUN: not %target-swiftc_driver -swift-version 4.3 %s 2>&1 | %FileCheck --check-prefix FIXIT_4 %s
// RUN: not %target-swiftc_driver -swift-version 3 -typecheck %s 2>&1 | %FileCheck --check-prefix ERROR_3 %s
// RUN: not %target-swiftc_driver -swift-version 4 -typecheck %s 2>&1 | %FileCheck --check-prefix ERROR_4 %s
// BAD: invalid value // BAD: invalid value
// BAD: note: valid arguments to '-swift-version' are '3', '4' // BAD: note: valid arguments to '-swift-version' are '3', '4'
// FIXIT_3: use major version, as in '-swift-version 3' // FIXIT_3: use major version, as in '-swift-version 3'
// FIXIT_4: use major version, as in '-swift-version 4' // FIXIT_4: use major version, as in '-swift-version 4'
let x = 1
#if swift(>=3)
asdf
// ERROR_3: [[@LINE-1]]:1: error: {{use of unresolved identifier}}
// ERROR_4: [[@LINE-2]]:1: error: {{use of unresolved identifier}}
#else
jkl
#endif
#if swift(>=3.1)
asdf
// ERROR_3: [[@LINE-1]]:1: error: {{use of unresolved identifier}}
// ERROR_4: [[@LINE-2]]:1: error: {{use of unresolved identifier}}
#else
jkl
#endif
#if swift(>=4)
asdf // ERROR_4: [[@LINE]]:1: error: {{use of unresolved identifier}}
#else
jkl // ERROR_3: [[@LINE]]:1: error: {{use of unresolved identifier}}
#endif
#if swift(>=4.1)
asdf
#else
jkl
// ERROR_3: [[@LINE-1]]:1: error: {{use of unresolved identifier}}
// ERROR_4: [[@LINE-2]]:1: error: {{use of unresolved identifier}}
#endif
#if swift(>=5)
asdf
#else
jkl
// ERROR_3: [[@LINE-1]]:1: error: {{use of unresolved identifier}}
// ERROR_4: [[@LINE-2]]:1: error: {{use of unresolved identifier}}
#endif

View File

@@ -3491,15 +3491,19 @@ static int prepareForDump(const char *Main,
options::ModuleCachePath; options::ModuleCachePath;
if (!options::SwiftVersion.empty()) { if (!options::SwiftVersion.empty()) {
if (auto Version = version::Version:: using version::Version;
parseVersionString(options::SwiftVersion, SourceLoc(), nullptr)) { bool isValid = false;
if (Version.getValue().isValidEffectiveLanguageVersion()) if (auto Version = Version::parseVersionString(options::SwiftVersion,
InitInvok.getLangOptions().EffectiveLanguageVersion = Version.getValue(); SourceLoc(), nullptr)) {
else { if (auto Effective = Version.getValue().getEffectiveLanguageVersion()) {
llvm::errs() << "Unsupported Swift Version.\n"; InitInvok.getLangOptions().EffectiveLanguageVersion = *Effective;
return 1; isValid = true;
} }
} }
if (!isValid) {
llvm::errs() << "Unsupported Swift Version.\n";
return 1;
}
} }
if (!options::ResourceDir.empty()) { if (!options::ResourceDir.empty()) {

View File

@@ -2957,7 +2957,8 @@ int main(int argc, char *argv[]) {
if (auto swiftVersion = if (auto swiftVersion =
version::Version::parseVersionString(options::SwiftVersion, version::Version::parseVersionString(options::SwiftVersion,
SourceLoc(), nullptr)) { SourceLoc(), nullptr)) {
InitInvok.getLangOptions().EffectiveLanguageVersion = *swiftVersion; if (auto actual = swiftVersion.getValue().getEffectiveLanguageVersion())
InitInvok.getLangOptions().EffectiveLanguageVersion = actual.getValue();
} }
} }
InitInvok.getClangImporterOptions().ModuleCachePath = InitInvok.getClangImporterOptions().ModuleCachePath =