Skip to content

Commit e66095b

Browse files
committed
[Parser] Support "<" unary operator in #if swift() expressions
Until now, only ">=" was supported in #if swift() expressions, for example: ```#if swift(>=2.1) ```#endif This means that if we want to evaluate code only when the language version is less than a particular version we need to do the following: ```#if !swift(>=2.1) ```#endif An alernative to make this more readable (the "!" can be easily missed in a code review) is to introduce another supported unary operator, "<". The previous example could be rewritten like this: ```#if swift(<2.1) ```#endif This commit adds support for that unary operator, along with some tests.
1 parent a6952de commit e66095b

File tree

4 files changed

+63
-18
lines changed

4 files changed

+63
-18
lines changed

include/swift/Basic/Version.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class Version {
161161
};
162162

163163
bool operator>=(const Version &lhs, const Version &rhs);
164+
bool operator<(const Version &lhs, const Version &rhs);
164165
bool operator==(const Version &lhs, const Version &rhs);
165166
inline bool operator!=(const Version &lhs, const Version &rhs) {
166167
return !(lhs == rhs);

lib/Basic/Version.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,11 @@ bool operator>=(const class Version &lhs,
384384
return true;
385385
}
386386

387+
bool operator<(const class Version &lhs, const class Version &rhs) {
388+
389+
return !(lhs >= rhs);
390+
}
391+
387392
bool operator==(const class Version &lhs,
388393
const class Version &rhs) {
389394
auto n = std::max(lhs.size(), rhs.size());
@@ -446,4 +451,3 @@ std::string getSwiftRevision() {
446451

447452
} // end namespace version
448453
} // end namespace swift
449-

lib/Parse/ParseIfConfig.cpp

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace {
3636
/// Get PlatformConditionKind from platform condition name.
3737
static
3838
Optional<PlatformConditionKind> getPlatformConditionKind(StringRef Name) {
39-
return llvm::StringSwitch<llvm::Optional<PlatformConditionKind>>(Name)
39+
return llvm::StringSwitch<Optional<PlatformConditionKind>>(Name)
4040
.Case("os", PlatformConditionKind::OS)
4141
.Case("arch", PlatformConditionKind::Arch)
4242
.Case("_endian", PlatformConditionKind::Endianness)
@@ -53,6 +53,20 @@ static StringRef extractExprSource(SourceManager &SM, Expr *E) {
5353
return SM.extractText(Range);
5454
}
5555

56+
static bool isValidPrefixUnaryOperator(Optional<StringRef> UnaryOperator) {
57+
return UnaryOperator != None &&
58+
(UnaryOperator.getValue() == ">=" || UnaryOperator.getValue() == "<");
59+
}
60+
61+
static bool isValidVersion(const version::Version &Version,
62+
const version::Version &ExpectedVersion,
63+
StringRef UnaryOperator) {
64+
if (UnaryOperator == ">=")
65+
return Version >= ExpectedVersion;
66+
if (UnaryOperator == "<")
67+
return Version < ExpectedVersion;
68+
llvm_unreachable("unsupported unary operator");
69+
}
5670

5771
/// The condition validator.
5872
class ValidateIfConfigCondition :
@@ -63,7 +77,7 @@ class ValidateIfConfigCondition :
6377
bool HasError;
6478

6579
/// Get the identifier string of the UnresolvedDeclRefExpr.
66-
llvm::Optional<StringRef> getDeclRefStr(Expr *E, DeclRefKind Kind) {
80+
Optional<StringRef> getDeclRefStr(Expr *E, DeclRefKind Kind) {
6781
auto UDRE = dyn_cast<UnresolvedDeclRefExpr>(E);
6882
if (!UDRE ||
6983
!UDRE->hasName() ||
@@ -93,7 +107,7 @@ class ValidateIfConfigCondition :
93107
Expr *foldSequence(Expr *LHS, ArrayRef<Expr*> &S, bool isRecurse = false) {
94108
assert(!S.empty() && ((S.size() & 1) == 0));
95109

96-
auto getNextOperator = [&]() -> llvm::Optional<StringRef> {
110+
auto getNextOperator = [&]() -> Optional<StringRef> {
97111
assert((S.size() & 1) == 0);
98112
while (!S.empty()) {
99113
auto Name = getDeclRefStr(S[0], DeclRefKind::BinaryOperator);
@@ -117,7 +131,7 @@ class ValidateIfConfigCondition :
117131
// If failed, it's not a sequence anymore.
118132
return LHS;
119133
Expr *Op = S[0];
120-
134+
121135
// We will definitely be consuming at least one operator.
122136
// Pull out the prospective RHS and slice off the first two elements.
123137
Expr *RHS = validate(S[1]);
@@ -277,16 +291,16 @@ class ValidateIfConfigCondition :
277291
return E;
278292
}
279293

280-
// 'swift' '(' '>=' float-literal ( '.' integer-literal )* ')'
281-
// 'compiler' '(' '>=' float-literal ( '.' integer-literal )* ')'
294+
// 'swift' '(' ('>=' | '<') float-literal ( '.' integer-literal )* ')'
295+
// 'compiler' '(' ('>=' | '<') float-literal ( '.' integer-literal )* ')'
282296
if (*KindName == "swift" || *KindName == "compiler") {
283297
auto PUE = dyn_cast<PrefixUnaryExpr>(Arg);
284-
llvm::Optional<StringRef> PrefixName = PUE ?
285-
getDeclRefStr(PUE->getFn(), DeclRefKind::PrefixOperator) : None;
286-
if (!PrefixName || *PrefixName != ">=") {
287-
D.diagnose(Arg->getLoc(),
288-
diag::unsupported_platform_condition_argument,
289-
"a unary comparison, such as '>=2.2'");
298+
Optional<StringRef> PrefixName =
299+
PUE ? getDeclRefStr(PUE->getFn(), DeclRefKind::PrefixOperator) : None;
300+
if (!isValidPrefixUnaryOperator(PrefixName)) {
301+
D.diagnose(
302+
Arg->getLoc(), diag::unsupported_platform_condition_argument,
303+
"a unary comparison '>=' or '<'; for example, '>=2.2' or '<2.2'");
290304
return nullptr;
291305
}
292306
auto versionString = extractExprSource(Ctx.SourceMgr, PUE->getArg());
@@ -448,13 +462,17 @@ class EvaluateIfConfigCondition :
448462
return thisVersion >= Val;
449463
} else if ((KindName == "swift") || (KindName == "compiler")) {
450464
auto PUE = cast<PrefixUnaryExpr>(Arg);
465+
auto PrefixName = getDeclRefStr(PUE->getFn());
451466
auto Str = extractExprSource(Ctx.SourceMgr, PUE->getArg());
452467
auto Val = version::Version::parseVersionString(
453468
Str, SourceLoc(), nullptr).getValue();
454469
if (KindName == "swift") {
455-
return Ctx.LangOpts.EffectiveLanguageVersion >= Val;
470+
return isValidVersion(Ctx.LangOpts.EffectiveLanguageVersion, Val,
471+
PrefixName);
456472
} else if (KindName == "compiler") {
457-
return version::Version::getCurrentLanguageVersion() >= Val;
473+
auto currentLanguageVersion =
474+
version::Version::getCurrentLanguageVersion();
475+
return isValidVersion(currentLanguageVersion, Val, PrefixName);
458476
} else {
459477
llvm_unreachable("unsupported version conditional");
460478
}

test/Parse/ConditionalCompilation/language_version.swift

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@
77
asdf asdf asdf asdf
88
#endif
99

10+
#if swift(<1.2)
11+
#endif
12+
13+
#if swift(<4.2)
14+
let a = 1
15+
#else
16+
let a = 2
17+
#endif
18+
19+
#if swift(<1.0)
20+
// This shouldn't emit any diagnostics.
21+
asdf asdf asdf asdf
22+
#endif
23+
1024
#if swift(>=1.2)
1125

1226
#if os(iOS)
@@ -34,13 +48,21 @@
3448
%#^*&
3549
#endif
3650

37-
#if swift(">=7.1") // expected-error {{unexpected platform condition argument: expected a unary comparison, such as '>=2.2'}}
51+
#if !swift(<1000.0)
52+
// This shouldn't emit any diagnostics.
53+
%#^*&
54+
#endif
55+
56+
#if swift(">=7.1") // expected-error {{unexpected platform condition argument: expected a unary comparison '>=' or '<'; for example, '>=2.2' or '<2.2'}}
57+
#endif
58+
59+
#if swift("<7.1") // expected-error {{unexpected platform condition argument: expected a unary comparison '>=' or '<'; for example, '>=2.2' or '<2.2'}}
3860
#endif
3961

40-
#if swift(">=2n.2") // expected-error {{unexpected platform condition argument: expected a unary comparison, such as '>=2.2'}}
62+
#if swift(">=2n.2") // expected-error {{unexpected platform condition argument: expected a unary comparison '>=' or '<'; for example, '>=2.2' or '<2.2'}}
4163
#endif
4264

43-
#if swift("") // expected-error {{unexpected platform condition argument: expected a unary comparison, such as '>=2.2'}}
65+
#if swift("") // expected-error {{unexpected platform condition argument: expected a unary comparison '>=' or '<'; for example, '>=2.2' or '<2.2'}}
4466
#endif
4567

4668
#if swift(>=2.2.1)

0 commit comments

Comments
 (0)