Skip to content

Commit e04c7fd

Browse files
authored
Merge pull request #12964 from graydon/if-target-environment
#if targetEnvironment(simulator)
2 parents cc9047d + 94988a2 commit e04c7fd

File tree

7 files changed

+192
-10
lines changed

7 files changed

+192
-10
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,10 @@ ERROR(empty_version_string,none,
15031503
WARNING(unknown_platform_condition_argument,none,
15041504
"unknown %0 for build configuration '%1'",
15051505
(StringRef, StringRef))
1506+
WARNING(likely_simulator_platform_condition,none,
1507+
"plaform condition appears to be testing for simulator environment; "
1508+
"use 'targetEnvironment(simulator)' instead",
1509+
())
15061510

15071511
//------------------------------------------------------------------------------
15081512
// Availability query parsing diagnostics

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ namespace swift {
4646
Runtime,
4747
/// Conditional import of module
4848
CanImport,
49+
/// Target Environment (currently just 'simulator' or absent)
50+
TargetEnvironment,
4951
};
5052

5153
/// Describes which Swift 3 Objective-C inference warnings should be
@@ -354,7 +356,7 @@ namespace swift {
354356
}
355357

356358
private:
357-
llvm::SmallVector<std::pair<PlatformConditionKind, std::string>, 4>
359+
llvm::SmallVector<std::pair<PlatformConditionKind, std::string>, 5>
358360
PlatformConditionValues;
359361
llvm::SmallVector<std::string, 2> CustomConditionalCompilationFlags;
360362
};

lib/Basic/LangOptions.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "swift/Basic/LangOptions.h"
19+
#include "swift/Basic/Platform.h"
1920
#include "swift/Basic/Range.h"
2021
#include "swift/Config.h"
2122
#include "llvm/ADT/Hashing.h"
@@ -60,6 +61,10 @@ static const StringRef SupportedConditionalCompilationRuntimes[] = {
6061
"_Native",
6162
};
6263

64+
static const StringRef SupportedConditionalCompilationTargetEnvironments[] = {
65+
"simulator",
66+
};
67+
6368
template <size_t N>
6469
bool contains(const StringRef (&Array)[N], const StringRef &V,
6570
std::vector<StringRef> &suggestions) {
@@ -99,6 +104,9 @@ checkPlatformConditionSupported(PlatformConditionKind Kind, StringRef Value,
99104
case PlatformConditionKind::Runtime:
100105
return contains(SupportedConditionalCompilationRuntimes, Value,
101106
suggestions);
107+
case PlatformConditionKind::TargetEnvironment:
108+
return contains(SupportedConditionalCompilationTargetEnvironments, Value,
109+
suggestions);
102110
case PlatformConditionKind::CanImport:
103111
// All importable names are valid.
104112
// FIXME: Perform some kind of validation of the string?
@@ -254,6 +262,13 @@ std::pair<bool, bool> LangOptions::setTarget(llvm::Triple triple) {
254262
else
255263
addPlatformConditionValue(PlatformConditionKind::Runtime, "_Native");
256264

265+
// Set the "targetEnvironment" platform condition if targeting a simulator
266+
// environmet. Otherwise _no_ value is present for targetEnvironment; it's
267+
// an optional disambiguating refinement of the triple.
268+
if (swift::tripleIsAnySimulator(Target))
269+
addPlatformConditionValue(PlatformConditionKind::TargetEnvironment,
270+
"simulator");
271+
257272
// If you add anything to this list, change the default size of
258273
// PlatformConditionValues to not require an extra allocation
259274
// in the common case.

lib/Basic/Platform.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,37 @@ using namespace swift;
1818
bool swift::tripleIsiOSSimulator(const llvm::Triple &triple) {
1919
llvm::Triple::ArchType arch = triple.getArch();
2020
return (triple.isiOS() &&
21-
(arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64));
21+
// FIXME: transitional, this should eventually stop testing arch, and
22+
// switch to only checking the -environment field.
23+
(triple.isSimulatorEnvironment() ||
24+
arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64));
2225
}
2326

2427
bool swift::tripleIsAppleTVSimulator(const llvm::Triple &triple) {
2528
llvm::Triple::ArchType arch = triple.getArch();
2629
return (triple.isTvOS() &&
27-
(arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64));
30+
// FIXME: transitional, this should eventually stop testing arch, and
31+
// switch to only checking the -environment field.
32+
(triple.isSimulatorEnvironment() ||
33+
arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64));
2834
}
2935

3036
bool swift::tripleIsWatchSimulator(const llvm::Triple &triple) {
3137
llvm::Triple::ArchType arch = triple.getArch();
3238
return (triple.isWatchOS() &&
33-
(arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64));
39+
// FIXME: transitional, this should eventually stop testing arch, and
40+
// switch to only checking the -environment field.
41+
(triple.isSimulatorEnvironment() ||
42+
arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64));
3443
}
3544

3645
bool swift::tripleIsAnySimulator(const llvm::Triple &triple) {
37-
return tripleIsiOSSimulator(triple) ||
38-
tripleIsWatchSimulator(triple) ||
39-
tripleIsAppleTVSimulator(triple);
46+
// FIXME: transitional, this should eventually just use the -environment
47+
// field.
48+
return triple.isSimulatorEnvironment() ||
49+
tripleIsiOSSimulator(triple) ||
50+
tripleIsWatchSimulator(triple) ||
51+
tripleIsAppleTVSimulator(triple);
4052
}
4153

4254
DarwinPlatformKind swift::getDarwinPlatformKind(const llvm::Triple &triple) {

lib/Parse/ParseIfConfig.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Optional<PlatformConditionKind> getPlatformConditionKind(StringRef Name) {
3737
.Case("_endian", PlatformConditionKind::Endianness)
3838
.Case("_runtime", PlatformConditionKind::Runtime)
3939
.Case("canImport", PlatformConditionKind::CanImport)
40+
.Case("targetEnvironment", PlatformConditionKind::TargetEnvironment)
4041
.Default(None);
4142
}
4243

@@ -325,6 +326,8 @@ class ValidateIfConfigCondition :
325326
DiagName = "endianness"; break;
326327
case PlatformConditionKind::CanImport:
327328
DiagName = "import conditional"; break;
329+
case PlatformConditionKind::TargetEnvironment:
330+
DiagName = "target environment"; break;
328331
case PlatformConditionKind::Runtime:
329332
llvm_unreachable("handled above");
330333
}
@@ -544,8 +547,97 @@ static bool isVersionIfConfigCondition(Expr *Condition) {
544547
return IsVersionIfConfigCondition().visit(Condition);
545548
}
546549

550+
/// Get the identifier string from an \c Expr if it's an
551+
/// \c UnresolvedDeclRefExpr, otherwise the empty string.
552+
static StringRef getDeclRefStr(Expr *E) {
553+
if (auto *UDRE = cast<UnresolvedDeclRefExpr>(E)) {
554+
return UDRE->getName().getBaseIdentifier().str();
555+
}
556+
return "";
557+
}
558+
559+
static bool isPlatformConditionDisjunction(Expr *E, PlatformConditionKind Kind,
560+
ArrayRef<StringRef> Vals) {
561+
if (auto *Or = dyn_cast<BinaryExpr>(E)) {
562+
if (getDeclRefStr(Or->getFn()) == "||") {
563+
auto Args = Or->getArg()->getElements();
564+
return (isPlatformConditionDisjunction(Args[0], Kind, Vals) &&
565+
isPlatformConditionDisjunction(Args[1], Kind, Vals));
566+
}
567+
} else if (auto *P = dyn_cast<ParenExpr>(E)) {
568+
return isPlatformConditionDisjunction(P->getSubExpr(), Kind, Vals);
569+
} else if (auto *C = dyn_cast<CallExpr>(E)) {
570+
if (getPlatformConditionKind(getDeclRefStr(C->getFn())) != Kind)
571+
return false;
572+
if (auto *ArgP = dyn_cast<ParenExpr>(C->getArg())) {
573+
if (auto *Arg = ArgP->getSubExpr()) {
574+
auto ArgStr = getDeclRefStr(Arg);
575+
for (auto V : Vals) {
576+
if (ArgStr == V)
577+
return true;
578+
}
579+
}
580+
}
581+
}
582+
return false;
583+
}
584+
585+
// Search for the first occurrence of a _likely_ (but not definite) implicit
586+
// simulator-environment platform condition, or negation thereof. This is
587+
// defined as any logical conjunction of one or more os() platform conditions
588+
// _strictly_ from the set {iOS, tvOS, watchOS} and one or more arch() platform
589+
// conditions _strictly_ from the set {i386, x86_64}.
590+
//
591+
// These are (at the time of writing) defined as de-facto simulators in
592+
// Platform.cpp, and if a user is testing them they're _likely_ looking for
593+
// simulator-ness indirectly. If there is anything else in the condition aside
594+
// from these conditions (or the negation of such a conjunction), we
595+
// conservatively assume the user is testing something other than
596+
// simulator-ness.
597+
static Expr *findAnyLikelySimulatorEnvironmentTest(Expr *Condition) {
598+
599+
if (!Condition)
600+
return nullptr;
601+
602+
if (auto *N = dyn_cast<PrefixUnaryExpr>(Condition)) {
603+
return findAnyLikelySimulatorEnvironmentTest(N->getArg());
604+
} else if (auto *P = dyn_cast<ParenExpr>(Condition)) {
605+
return findAnyLikelySimulatorEnvironmentTest(P->getSubExpr());
606+
}
607+
608+
// We assume the user is writing the condition in CNF -- say (os(iOS) ||
609+
// os(tvOS)) && (arch(i386) || arch(x86_64)) -- rather than DNF, as the former
610+
// is exponentially more terse, and these conditions are already quite
611+
// unwieldy. If field evidence shows people using other variants, possibly add
612+
// them here.
613+
614+
auto isSimulatorPlatformOSTest = [](Expr *E) -> bool {
615+
return isPlatformConditionDisjunction(
616+
E, PlatformConditionKind::OS, {"iOS", "tvOS", "watchOS"});
617+
};
618+
619+
auto isSimulatorPlatformArchTest = [](Expr *E) -> bool {
620+
return isPlatformConditionDisjunction(
621+
E, PlatformConditionKind::Arch, {"i386", "x86_64"});
622+
};
623+
624+
if (auto *And = dyn_cast<BinaryExpr>(Condition)) {
625+
if (getDeclRefStr(And->getFn()) == "&&") {
626+
auto Args = And->getArg()->getElements();
627+
if ((isSimulatorPlatformOSTest(Args[0]) &&
628+
isSimulatorPlatformArchTest(Args[1])) ||
629+
(isSimulatorPlatformOSTest(Args[1]) &&
630+
isSimulatorPlatformArchTest(Args[0]))) {
631+
return And;
632+
}
633+
}
634+
}
635+
return nullptr;
636+
}
637+
547638
} // end anonymous namespace
548639

640+
549641
/// Parse and populate a #if ... #endif directive.
550642
/// Delegate callback function to parse elements in the blocks.
551643
ParserResult<IfConfigDecl> Parser::parseIfConfig(
@@ -594,6 +686,13 @@ ParserResult<IfConfigDecl> Parser::parseIfConfig(
594686
diag::extra_tokens_conditional_compilation_directive);
595687
}
596688

689+
if (Expr *Test = findAnyLikelySimulatorEnvironmentTest(Condition)) {
690+
diagnose(Test->getLoc(),
691+
diag::likely_simulator_platform_condition)
692+
.fixItReplace(Test->getSourceRange(),
693+
"targetEnvironment(simulator)");
694+
}
695+
597696
// Parse elements
598697
SmallVector<ASTNode, 16> Elements;
599698
if (isActive || !isVersionCondition) {

test/Parse/ConditionalCompilation/identifierName.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ func f2(
66
FOO: Int,
77
swift: Int, _compiler_version: Int,
88
os: Int, arch: Int, _endian: Int, _runtime: Int,
9+
targetEnvironment: Int,
910
arm: Int, i386: Int, macOS: Int, OSX: Int, Linux: Int,
1011
big: Int, little: Int,
11-
_ObjC: Int, _Native: Int
12+
_ObjC: Int, _Native: Int,
13+
simulator: Int
1214
) {
1315

1416
#if FOO
@@ -23,6 +25,8 @@ func f2(
2325
_ = _runtime + _ObjC + _Native
2426
#elseif swift(>=1.0) && _compiler_version("3.*.0")
2527
_ = swift + _compiler_version
28+
#elseif targetEnvironment(simulator)
29+
_ = targetEnvironment + simulator
2630
#endif
2731

2832
}
@@ -31,9 +35,11 @@ func f2() {
3135
let
3236
FOO = 1, swift = 1, _compiler_version = 1,
3337
os = 1, arch = 1, _endian = 1, _runtime = 1,
38+
targetEnvironment = 1,
3439
arm = 1, i386 = 1, macOS = 1, OSX = 1, Linux = 1,
3540
big = 1, little = 1,
36-
_ObjC = 1, _Native = 1
41+
_ObjC = 1, _Native = 1,
42+
simulator = 1
3743

3844
#if FOO
3945
_ = FOO
@@ -47,6 +53,8 @@ func f2() {
4753
_ = _runtime + _ObjC + _Native
4854
#elseif swift(>=1.0) && _compiler_version("3.*.0")
4955
_ = swift + _compiler_version
56+
#elseif targetEnvironment(simulator)
57+
_ = targetEnvironment + simulator
5058
#endif
5159

5260
}
@@ -55,16 +63,19 @@ struct S {
5563
let
5664
FOO = 1, swift = 1, _compiler_version = 1,
5765
os = 1, arch = 1, _endian = 1, _runtime = 1,
66+
targetEnvironment = 1,
5867
arm = 1, i386 = 1, macOS = 1, OSX = 1, Linux = 1,
5968
big = 1, little = 1,
60-
_ObjC = 1, _Native = 1
69+
_ObjC = 1, _Native = 1,
70+
simulator = 1
6171

6272
#if FOO
6373
#elseif os(macOS) && os(OSX) && os(Linux)
6474
#elseif arch(i386) && arch(arm)
6575
#elseif _endian(big) && _endian(little)
6676
#elseif _runtime(_ObjC) && _runtime(_Native)
6777
#elseif swift(>=1.0) && _compiler_version("3.*.0")
78+
#elseif targetEnvironment(simulator)
6879
#endif
6980

7081
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %swift -swift-version 4 -typecheck %s -verify -target x86_64-apple-ios7.0 -parse-stdlib
2+
// RUN: %swift -swift-version 4 -typecheck %s -verify -target x86_64-unknown-linux-simulator -parse-stdlib
3+
// RUN: %swift-ide-test -swift-version 4 -test-input-complete -source-filename=%s -target x86_64-apple-ios7.0
4+
5+
#if !targetEnvironment(simulator)
6+
// This block should not parse.
7+
let i: Int = "Hello"
8+
#endif
9+
10+
#if targetEnvironment(simulator)
11+
class C {}
12+
var x = C()
13+
#endif
14+
var y = x
15+
16+
#if os(iOS) && arch(i386)
17+
// expected-warning @-1 {{plaform condition appears to be testing for simulator environment}} {{5-26=targetEnvironment(simulator)}}
18+
class C1 {}
19+
#endif
20+
21+
#if arch(i386) && os(iOS)
22+
// expected-warning @-1 {{plaform condition appears to be testing for simulator environment}} {{5-26=targetEnvironment(simulator)}}
23+
class C2 {}
24+
#endif
25+
26+
#if arch(i386) && (os(iOS) || os(watchOS))
27+
// expected-warning @-1 {{plaform condition appears to be testing for simulator environment}} {{5-43=targetEnvironment(simulator)}}
28+
class C3 {}
29+
#endif
30+
31+
#if (arch(x86_64) || arch(i386)) && (os(iOS) || os(watchOS) || os(tvOS))
32+
// expected-warning @-1 {{plaform condition appears to be testing for simulator environment}} {{5-73=targetEnvironment(simulator)}}
33+
class C4 {}
34+
#endif
35+
36+
#if !(arch(x86_64) && os(tvOS))
37+
// expected-warning @-1 {{plaform condition appears to be testing for simulator environment}} {{7-31=targetEnvironment(simulator)}}
38+
class C5 {}
39+
#endif

0 commit comments

Comments
 (0)