Skip to content

Commit 645d73f

Browse files
authored
Merge pull request #58480 from beccadax/so-i-put-versions-in-your-version-numbers
Update SWIFT_COMPILER_VERSION language features
2 parents 050f3e2 + 3843c7c commit 645d73f

File tree

7 files changed

+142
-34
lines changed

7 files changed

+142
-34
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1816,7 +1816,10 @@ ERROR(version_component_not_number,none,
18161816
ERROR(compiler_version_too_many_components,none,
18171817
"compiler version must not have more than five components", ())
18181818
WARNING(unused_compiler_version_component,NoUsage,
1819-
"the second version component is not used for comparison", ())
1819+
"the second version component is not used for comparison in legacy "
1820+
"compiler versions%select{|; are you trying to encode a new Swift "
1821+
"compiler version for compatibility with legacy compilers?}0",
1822+
(bool))
18201823
ERROR(empty_version_component,none,
18211824
"found empty version component", ())
18221825
ERROR(compiler_version_component_out_of_range,none,

include/swift/Basic/Version.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,12 @@ class Version {
129129
/// Return this Version struct as the appropriate version string for APINotes.
130130
std::string asAPINotesVersionString() const;
131131

132-
/// Parse a version in the form used by the _compiler_version \#if condition.
132+
/// Parse a version in the form used by the _compiler_version(string-literal)
133+
/// \#if condition.
134+
///
135+
/// \note This is \em only used for the string literal version, so it includes
136+
/// backwards-compatibility logic to convert it to something that can be
137+
/// compared with a modern SWIFT_COMPILER_VERSION.
133138
static Optional<Version> parseCompilerVersionString(StringRef VersionString,
134139
SourceLoc Loc,
135140
DiagnosticEngine *Diags);

lib/Basic/Version.cpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "clang/Basic/CharInfo.h"
1818
#include "llvm/Support/raw_ostream.h"
19+
#include "llvm/Support/FormatVariadic.h"
1920
#include "llvm/ADT/SmallString.h"
2021
#include "llvm/ADT/StringExtras.h"
2122
#include "swift/AST/DiagnosticsParse.h"
@@ -132,9 +133,29 @@ Optional<Version> Version::parseCompilerVersionString(
132133
// The second version component isn't used for comparison.
133134
if (i == 1) {
134135
if (!SplitComponent.equals("*")) {
135-
if (Diags)
136-
Diags->diagnose(Range.Start, diag::unused_compiler_version_component)
137-
.fixItReplaceChars(Range.Start, Range.End, "*");
136+
if (Diags) {
137+
// Majors 600-1300 were used for Swift 1.0-5.5 (based on clang
138+
// versions), but then we reset the numbering based on Swift versions,
139+
// so 5.6 had major 5. We assume that majors below 600 use the new
140+
// scheme and equal/above it use the old scheme.
141+
bool firstComponentLooksNew = CV.Components[0] < 600;
142+
143+
auto diag = Diags->diagnose(Range.Start,
144+
diag::unused_compiler_version_component,
145+
firstComponentLooksNew);
146+
147+
if (firstComponentLooksNew &&
148+
!SplitComponent.getAsInteger(10, ComponentNumber)) {
149+
// Fix-it version like "5.7.1.2.3" to "5007.*.1.2.3".
150+
auto newDigits = llvm::formatv("{0}{1,0+3}.*", CV.Components[0],
151+
ComponentNumber).str();
152+
diag.fixItReplaceChars(SplitComponents[0].second.Start,
153+
Range.End, newDigits);
154+
}
155+
else {
156+
diag.fixItReplaceChars(Range.Start, Range.End, "*");
157+
}
158+
}
138159
}
139160

140161
CV.Components.push_back(0);
@@ -159,6 +180,38 @@ Optional<Version> Version::parseCompilerVersionString(
159180
isValidVersion = false;
160181
}
161182

183+
// In the beginning, '_compiler_version(string-literal)' was designed for a
184+
// different version scheme where the major was fairly large and the minor
185+
// was ignored; now we use one where the minor is significant and major and
186+
// minor match the Swift language version. See the comment above on
187+
// `firstComponentLooksNew` for details.
188+
//
189+
// However, we want the string literal variant of '_compiler_version' to
190+
// maintain source compatibility with old checks; that means checks for new
191+
// versions have to be written so that old compilers will think they represent
192+
// newer versions, while new compilers have to interpret old version number
193+
// strings in a way that will compare correctly to the new versions compiled
194+
// into them.
195+
//
196+
// To achieve this, modern compilers divide the major by 1000 and overwrite
197+
// the wildcard component with the remainder, effectively shifting the last
198+
// three digits of the major into the minor, before comparing it to the
199+
// compiler version:
200+
//
201+
// _compiler_version("5007.*.1.2.3") -> 5.7.1.2.3
202+
// _compiler_version("1300.*.1.2.3") -> 1.300.1.2.3 (smaller than 5.6)
203+
// _compiler_version( "600.*.1.2.3") -> 0.600.1.2.3 (smaller than 5.6)
204+
//
205+
// So if you want to specify a 5.7.z.a.b version, we ask users to either write
206+
// it as 5007.*.z.a.b, or to use the new '_compiler_version(>= version)'
207+
// syntax instead, which does not perform this conversion.
208+
if (!CV.Components.empty()) {
209+
if (CV.Components.size() == 1)
210+
CV.Components.push_back(0);
211+
CV.Components[1] = CV.Components[0] % 1000;
212+
CV.Components[0] = CV.Components[0] / 1000;
213+
}
214+
162215
return isValidVersion ? Optional<Version>(CV) : None;
163216
}
164217

lib/ClangImporter/ClangImporter.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,9 +651,15 @@ importer::getNormalInvocationArguments(
651651
// declarations.
652652
auto V = version::Version::getCurrentCompilerVersion();
653653
if (!V.empty()) {
654+
// Note: Prior to Swift 5.7, the "Y" version component was omitted and the
655+
// "X" component resided in its digits.
654656
invocationArgStrs.insert(invocationArgStrs.end(), {
655657
V.preprocessorDefinition("__SWIFT_COMPILER_VERSION",
656-
{1000000000, /*ignored*/ 0, 1000000, 1000, 1}),
658+
{1000000000000, // X
659+
1000000000, // Y
660+
1000000, // Z
661+
1000, // a
662+
1}), // b
657663
});
658664
}
659665
} else {

lib/Parse/ParseIfConfig.cpp

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -283,30 +283,26 @@ class ValidateIfConfigCondition :
283283
}
284284
// '_compiler_version' '(' string-literal ')'
285285
if (*KindName == "_compiler_version") {
286-
auto SLE = dyn_cast<StringLiteralExpr>(Arg);
287-
if (!SLE) {
288-
D.diagnose(Arg->getLoc(),
289-
diag::unsupported_platform_condition_argument,
290-
"string literal");
291-
return nullptr;
292-
}
293-
294-
auto ValStr = SLE->getValue();
295-
if (ValStr.empty()) {
296-
D.diagnose(SLE->getLoc(), diag::empty_version_string);
297-
return nullptr;
286+
if (auto SLE = dyn_cast<StringLiteralExpr>(Arg)) {
287+
auto ValStr = SLE->getValue();
288+
if (ValStr.empty()) {
289+
D.diagnose(SLE->getLoc(), diag::empty_version_string);
290+
return nullptr;
291+
}
292+
293+
auto Val = version::Version::parseCompilerVersionString(
294+
SLE->getValue(), SLE->getLoc(), &D);
295+
if (!Val.hasValue())
296+
return nullptr;
297+
return E;
298298
}
299-
300-
auto Val = version::Version::parseCompilerVersionString(
301-
SLE->getValue(), SLE->getLoc(), &D);
302-
if (!Val.hasValue())
303-
return nullptr;
304-
return E;
305299
}
306300

307301
// 'swift' '(' ('>=' | '<') float-literal ( '.' integer-literal )* ')'
308302
// 'compiler' '(' ('>=' | '<') float-literal ( '.' integer-literal )* ')'
309-
if (*KindName == "swift" || *KindName == "compiler") {
303+
// '_compiler_version' '(' ('>=' | '<') float-literal ( '.' integer-literal )* ')'
304+
if (*KindName == "swift" || *KindName == "compiler" ||
305+
*KindName == "_compiler_version") {
310306
auto PUE = dyn_cast<PrefixUnaryExpr>(Arg);
311307
Optional<StringRef> PrefixName =
312308
PUE ? getDeclRefStr(PUE->getFn(), DeclRefKind::PrefixOperator) : None;
@@ -500,28 +496,30 @@ class EvaluateIfConfigCondition :
500496
bool visitCallExpr(CallExpr *E) {
501497
auto KindName = getDeclRefStr(E->getFn());
502498
auto *Arg = getSingleSubExp(E->getArgs(), KindName, nullptr);
503-
if (KindName == "_compiler_version") {
499+
if (KindName == "_compiler_version" && isa<StringLiteralExpr>(Arg)) {
504500
auto Str = cast<StringLiteralExpr>(Arg)->getValue();
505501
auto Val = version::Version::parseCompilerVersionString(
506502
Str, SourceLoc(), nullptr).getValue();
507503
auto thisVersion = version::Version::getCurrentCompilerVersion();
508504
return thisVersion >= Val;
509-
} else if ((KindName == "swift") || (KindName == "compiler")) {
505+
} else if ((KindName == "swift") || (KindName == "compiler") ||
506+
(KindName == "_compiler_version")) {
510507
auto PUE = cast<PrefixUnaryExpr>(Arg);
511508
auto PrefixName = getDeclRefStr(PUE->getFn());
512509
auto Str = extractExprSource(Ctx.SourceMgr, PUE->getOperand());
513510
auto Val = version::Version::parseVersionString(
514511
Str, SourceLoc(), nullptr).getValue();
512+
version::Version thisVersion;
515513
if (KindName == "swift") {
516-
return isValidVersion(Ctx.LangOpts.EffectiveLanguageVersion, Val,
517-
PrefixName);
514+
thisVersion = Ctx.LangOpts.EffectiveLanguageVersion;
518515
} else if (KindName == "compiler") {
519-
auto currentLanguageVersion =
520-
version::Version::getCurrentLanguageVersion();
521-
return isValidVersion(currentLanguageVersion, Val, PrefixName);
516+
thisVersion = version::Version::getCurrentLanguageVersion();
517+
} else if (KindName == "_compiler_version") {
518+
thisVersion = version::Version::getCurrentCompilerVersion();
522519
} else {
523520
llvm_unreachable("unsupported version conditional");
524521
}
522+
return isValidVersion(thisVersion, Val, PrefixName);
525523
} else if (KindName == "canImport") {
526524
auto Str = extractExprSource(Ctx.SourceMgr, Arg);
527525
bool underlyingModule = false;

test/Parse/ConditionalCompilation/compiler_version.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
asdf asdf asdf asdf
88
#endif
99

10-
#if _compiler_version("10.*.10.10")
10+
#if _compiler_version("600.*.10.10")
1111

1212
#if os(iOS)
1313
let z = 1
@@ -48,7 +48,10 @@
4848
let thisWillStillParseBecauseConfigIsError = 1
4949
#endif
5050

51-
#if _compiler_version("700.0.100") // expected-warning {{the second version component is not used for comparison}}
51+
#if _compiler_version("700.0.100") // expected-warning {{the second version component is not used for comparison in legacy compiler versions}} {{28-29=*}}
52+
#endif
53+
54+
#if _compiler_version("5.7.100") // expected-warning {{the second version component is not used for comparison in legacy compiler versions; are you trying to encode a new Swift compiler version for compatibility with legacy compilers?}} {{24-27=5007.*}}
5255
#endif
5356

5457
#if _compiler_version("700.*.1.1.1.1") // expected-error {{version must not have more than five components}}
@@ -65,3 +68,14 @@
6568

6669
#if _compiler_version("700.*.1.1.1000") // expected-error {{version component out of range: must be in [0, 999]}}
6770
#endif
71+
72+
// New style _compiler_version()
73+
#if _compiler_version(<4.0)
74+
// This shouldn't emit any diagnostics.
75+
asdf asdf asdf asdf
76+
#endif
77+
78+
#if !_compiler_version(>=4.3.2.1.0)
79+
// This shouldn't emit any diagnostics.
80+
asdf asdf asdf asdf
81+
#endif

unittests/Parse/BuildConfigTests.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ using namespace llvm;
88

99
class CompilerVersionTest : public ::testing::Test {};
1010
class VersionTest : public ::testing::Test{};
11+
class CompilerVersionUnpackingTest : public ::testing::Test {};
1112

1213
Optional<version::Version> CV(const char *VersionString) {
1314
return version::Version::parseCompilerVersionString(VersionString,
@@ -52,3 +53,31 @@ TEST_F(VersionTest, VersionComparison) {
5253
EXPECT_FALSE(V(".1").hasValue());
5354

5455
}
56+
57+
TEST_F(CompilerVersionUnpackingTest, VersionComparison) {
58+
EXPECT_EQ(CV("700").getValue(), V("0.700").getValue());
59+
EXPECT_EQ(CV("700.*").getValue(), V("0.700").getValue());
60+
EXPECT_EQ(CV("700.*.1").getValue(), V("0.700.1").getValue());
61+
EXPECT_EQ(CV("700.*.23").getValue(), V("0.700.23").getValue());
62+
EXPECT_EQ(CV("700.*.1.1").getValue(), V("0.700.1.1").getValue());
63+
64+
EXPECT_EQ(CV("1300").getValue(), V("1.300").getValue());
65+
EXPECT_EQ(CV("1300.*").getValue(), V("1.300").getValue());
66+
EXPECT_EQ(CV("1300.*.1").getValue(), V("1.300.1").getValue());
67+
EXPECT_EQ(CV("1300.*.23").getValue(), V("1.300.23").getValue());
68+
EXPECT_EQ(CV("1300.*.1.1").getValue(), V("1.300.1.1").getValue());
69+
70+
EXPECT_EQ(CV("5007").getValue(), V("5.7").getValue());
71+
EXPECT_EQ(CV("5007.*").getValue(), V("5.7").getValue());
72+
EXPECT_EQ(CV("5007.*.1").getValue(), V("5.7.1").getValue());
73+
EXPECT_EQ(CV("5007.*.23").getValue(), V("5.7.23").getValue());
74+
EXPECT_EQ(CV("5007.*.1.1").getValue(), V("5.7.1.1").getValue());
75+
76+
// Since this test was added during 5.7, we expect all of these comparisons to
77+
// be GE, either because we are comparing to the empty version or because we
78+
// are comparing to a version >= 5.7.0.0.0.
79+
auto currentVersion = version::Version::getCurrentCompilerVersion();
80+
EXPECT_GE(CV("700"), currentVersion);
81+
EXPECT_GE(CV("1300"), currentVersion);
82+
EXPECT_GE(CV("5007"), currentVersion);
83+
}

0 commit comments

Comments
 (0)