Skip to content

Commit 0fdba62

Browse files
authored
Merge pull request #59055 from DougGregor/future-feature
[SE-0362] Piecemeal adoption of upcoming language improvements
2 parents 109d9c1 + d693ac3 commit 0fdba62

14 files changed

+165
-17
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ CHANGELOG
33

44
_**Note:** This is in reverse chronological order, so newer entries are added to the top._
55

6+
## Swift 5.8
7+
8+
* [SE-0362][]:
9+
10+
The compiler flag `-enable-upcoming-feature X` can now be used to enable a specific feature `X` that has been accepted by the evolution process, but whose introduction into the language is waiting for the next major version (e.g., version 6). The `X` is specified by any proposal that falls into this category:
11+
* `ConciseMagicFile` enables the new `#file` semantics in [SE-0274][].
12+
* `ForwardTrailingClosures` disables the "backward" scanning behavior of [SE-0286][].
13+
* `BareSlashRegexLiterals` enables the regex literal syntax of [SE-0352][].
14+
15+
Features can be detected in source code with `#if hasFeature(X)`.
16+
617
## Swift 5.7
718

819
* The Swift compiler no longer warns about redundant requirements in generic declarations. For example,
@@ -9459,6 +9470,7 @@ Swift 1.0
94599470
[SE-0267]: <https://github.com/apple/swift-evolution/blob/main/proposals/0267-where-on-contextually-generic.md>
94609471
[SE-0268]: <https://github.com/apple/swift-evolution/blob/main/proposals/0268-didset-semantics.md>
94619472
[SE-0269]: <https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md>
9473+
[SE-0274]: <https://github.com/apple/swift-evolution/blob/main/proposals/0274-magic-file.md>
94629474
[SE-0276]: <https://github.com/apple/swift-evolution/blob/main/proposals/0276-multi-pattern-catch-clauses.md>
94639475
[SE-0279]: <https://github.com/apple/swift-evolution/blob/main/proposals/0279-multiple-trailing-closures.md>
94649476
[SE-0280]: <https://github.com/apple/swift-evolution/blob/main/proposals/0280-enum-cases-as-protocol-witnesses.md>
@@ -9509,6 +9521,7 @@ Swift 1.0
95099521
[SE-0355]: <https://github.com/apple/swift-evolution/blob/main/proposals/0355-regex-syntax-run-time-construction.md>
95109522
[SE-0357]: <https://github.com/apple/swift-evolution/blob/main/proposals/0357-regex-string-processing-algorithms.md>
95119523
[SE-0358]: <https://github.com/apple/swift-evolution/blob/main/proposals/0358-primary-associated-types-in-stdlib.md>
9524+
[SE-0362]: <https://github.com/apple/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md>
95129525

95139526
[SR-75]: <https://bugs.swift.org/browse/SR-75>
95149527
[SR-106]: <https://bugs.swift.org/browse/SR-106>

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ ERROR(error_experimental_feature_not_available, none,
4040
"experimental feature '%0' cannot be enabled in a production compiler",
4141
(StringRef))
4242

43+
ERROR(error_upcoming_feature_on_by_default, none,
44+
"upcoming feature '%0' is already enabled as of Swift version %1",
45+
(StringRef, unsigned))
46+
4347
ERROR(error_unknown_library_level, none,
4448
"unknown library level '%0', "
4549
"expected one of 'api', 'spi' or 'other'", (StringRef))

include/swift/Basic/Feature.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ inline bool featureImpliesFeature(Feature feature, Feature implied) {
5353
}
5454

5555
/// Get the feature corresponding to this "future" feature, if there is one.
56-
llvm::Optional<Feature> getFutureFeature(llvm::StringRef name);
56+
llvm::Optional<Feature> getUpcomingFeature(llvm::StringRef name);
5757

5858
/// Get the feature corresponding to this "experimental" feature, if there is
5959
/// one.

include/swift/Basic/Features.def

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option)
4949
#endif
5050

51-
#ifndef FUTURE_FEATURE
52-
# define FUTURE_FEATURE(FeatureName, SENumber, Version) \
51+
#ifndef UPCOMING_FEATURE
52+
# define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
5353
LANGUAGE_FEATURE(FeatureName, SENumber, #FeatureName, \
5454
langOpts.hasFeature(#FeatureName))
5555
#endif
@@ -90,9 +90,9 @@ SUPPRESSIBLE_LANGUAGE_FEATURE(PrimaryAssociatedTypes2, 346, "Primary associated
9090
SUPPRESSIBLE_LANGUAGE_FEATURE(UnavailableFromAsync, 0, "@_unavailableFromAsync", true)
9191
SUPPRESSIBLE_LANGUAGE_FEATURE(NoAsyncAvailability, 340, "@available(*, noasync)", true)
9292

93-
FUTURE_FEATURE(ConciseMagicFile, 274, 6)
94-
FUTURE_FEATURE(ForwardTrailingClosures, 286, 6)
95-
FUTURE_FEATURE(BareSlashRegexLiterals, 354, 6)
93+
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
94+
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)
95+
UPCOMING_FEATURE(BareSlashRegexLiterals, 354, 6)
9696

9797
EXPERIMENTAL_FEATURE(StaticAssert)
9898
EXPERIMENTAL_FEATURE(VariadicGenerics)
@@ -118,6 +118,6 @@ EXPERIMENTAL_FEATURE(AdditiveArithmeticDerivedConformances)
118118
EXPERIMENTAL_FEATURE(SendableCompletionHandlers)
119119

120120
#undef EXPERIMENTAL_FEATURE
121-
#undef FUTURE_FEATURE
121+
#undef UPCOMING_FEATURE
122122
#undef SUPPRESSIBLE_LANGUAGE_FEATURE
123123
#undef LANGUAGE_FEATURE

include/swift/Basic/LangOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "llvm/ADT/SmallString.h"
2929
#include "llvm/ADT/SmallSet.h"
3030
#include "llvm/ADT/SmallVector.h"
31+
#include "llvm/ADT/SmallSet.h"
3132
#include "llvm/ADT/StringRef.h"
3233
#include "llvm/ADT/Triple.h"
3334
#include "llvm/Support/Regex.h"

include/swift/Option/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,11 @@ def enable_experimental_feature :
696696
Flags<[FrontendOption]>,
697697
HelpText<"Enable an experimental feature">;
698698

699+
def enable_upcoming_feature : Separate<["-"], "enable-upcoming-feature">,
700+
Flags<[FrontendOption]>,
701+
HelpText<"Enable a feature that will be introduced in an upcoming language "
702+
"version">;
703+
699704
def Rpass_EQ : Joined<["-"], "Rpass=">,
700705
Flags<[FrontendOption]>,
701706
HelpText<"Report performed transformations by optimization passes whose "

lib/Basic/LangOptions.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ bool LangOptions::hasFeature(Feature feature) const {
240240
}
241241

242242
bool LangOptions::hasFeature(llvm::StringRef featureName) const {
243-
if (auto feature = getFutureFeature(featureName))
243+
if (auto feature = getUpcomingFeature(featureName))
244244
return hasFeature(*feature);
245245

246246
if (auto feature = getExperimentalFeature(featureName))
@@ -434,10 +434,10 @@ bool swift::isSuppressibleFeature(Feature feature) {
434434
llvm_unreachable("covered switch");
435435
}
436436

437-
llvm::Optional<Feature> swift::getFutureFeature(llvm::StringRef name) {
437+
llvm::Optional<Feature> swift::getUpcomingFeature(llvm::StringRef name) {
438438
return llvm::StringSwitch<Optional<Feature>>(name)
439439
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option)
440-
#define FUTURE_FEATURE(FeatureName, SENumber, Version) \
440+
#define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
441441
.Case(#FeatureName, Feature::FeatureName)
442442
#include "swift/Basic/Features.def"
443443
.Default(None);
@@ -455,7 +455,7 @@ llvm::Optional<Feature> swift::getExperimentalFeature(llvm::StringRef name) {
455455
llvm::Optional<unsigned> swift::getFeatureLanguageVersion(Feature feature) {
456456
switch (feature) {
457457
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option)
458-
#define FUTURE_FEATURE(FeatureName, SENumber, Version) \
458+
#define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
459459
case Feature::FeatureName: return Version;
460460
#include "swift/Basic/Features.def"
461461
default: return None;

lib/Driver/ToolChains.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
237237
inputArgs.AddLastArg(arguments, options::OPT_warn_concurrency);
238238
inputArgs.AddLastArg(arguments, options::OPT_strict_concurrency);
239239
inputArgs.AddAllArgs(arguments, options::OPT_enable_experimental_feature);
240+
inputArgs.AddAllArgs(arguments, options::OPT_enable_upcoming_feature);
240241
inputArgs.AddLastArg(arguments, options::OPT_warn_implicit_overrides);
241242
inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit);
242243
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "ArgsToFrontendOptionsConverter.h"
1717
#include "swift/AST/DiagnosticsFrontend.h"
18+
#include "swift/Basic/Feature.h"
1819
#include "swift/Basic/Platform.h"
1920
#include "swift/Option/Options.h"
2021
#include "swift/Option/SanitizerOptions.h"
@@ -637,6 +638,26 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
637638
}
638639
}
639640

641+
// Map historical flags over to future features.
642+
for (const Arg *A : Args.filtered(OPT_enable_upcoming_feature)) {
643+
// Ignore unknown features.
644+
auto feature = getUpcomingFeature(A->getValue());
645+
if (!feature)
646+
continue;
647+
648+
// Check if this feature was introduced already in this language version.
649+
if (auto firstVersion = getFeatureLanguageVersion(*feature)) {
650+
if (Opts.isSwiftVersionAtLeast(*firstVersion)) {
651+
Diags.diagnose(SourceLoc(), diag::error_upcoming_feature_on_by_default,
652+
A->getValue(), *firstVersion);
653+
continue;
654+
}
655+
}
656+
657+
// Add the feature.
658+
Opts.Features.insert(*feature);
659+
}
660+
640661
// Map historical flags over to experimental features. We do this for all
641662
// compilers because that's how existing experimental feature flags work.
642663
if (Args.hasArg(OPT_enable_experimental_variadic_generics))

lib/Parse/ParseIfConfig.cpp

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ static Expr *getSingleSubExp(ArgumentList *args, StringRef kindName,
133133
return nullptr;
134134
}
135135

136+
/// Returns \c true if the condition is a version check.
137+
static bool isVersionIfConfigCondition(Expr *Condition);
138+
139+
/// Evaluate the condition.
140+
/// \c true if success, \c false if failed.
141+
static bool evaluateIfConfigCondition(Expr *Condition, ASTContext &Context);
142+
136143
/// The condition validator.
137144
class ValidateIfConfigCondition :
138145
public ExprVisitor<ValidateIfConfigCondition, Expr*> {
@@ -202,7 +209,7 @@ class ValidateIfConfigCondition :
202209

203210
// We will definitely be consuming at least one operator.
204211
// Pull out the prospective RHS and slice off the first two elements.
205-
Expr *RHS = validate(S[1]);
212+
Expr *RHS = S[1];
206213
S = S.slice(2);
207214

208215
while (true) {
@@ -226,7 +233,7 @@ class ValidateIfConfigCondition :
226233

227234
OpName = NextOpName;
228235
Op = S[0];
229-
RHS = validate(S[1]);
236+
RHS = S[1];
230237
S = S.slice(2);
231238
}
232239

@@ -329,6 +336,16 @@ class ValidateIfConfigCondition :
329336
return E;
330337
}
331338

339+
if (*KindName == "hasFeature") {
340+
if (!getDeclRefStr(Arg, DeclRefKind::Ordinary)) {
341+
D.diagnose(E->getLoc(), diag::unsupported_platform_condition_argument,
342+
"feature name");
343+
return nullptr;
344+
}
345+
346+
return E;
347+
}
348+
332349
// ( 'os' | 'arch' | '_endian' | '_runtime' ) '(' identifier ')''
333350
auto Kind = getPlatformConditionKind(*KindName);
334351
if (!Kind.hasValue()) {
@@ -416,14 +433,37 @@ class ValidateIfConfigCondition :
416433
return E;
417434
}
418435

436+
Expr *visitBinaryExpr(BinaryExpr *E) {
437+
auto OpName = getDeclRefStr(E->getFn(), DeclRefKind::BinaryOperator);
438+
if (auto lhs = validate(E->getLHS())) {
439+
// If the left-hand side is a versioned condition, skip evaluation of
440+
// the right-hand side if it won't ever affect the result.
441+
if (OpName && isVersionIfConfigCondition(lhs)) {
442+
assert(*OpName == "&&" || *OpName == "||");
443+
bool isLHSTrue = evaluateIfConfigCondition(lhs, Ctx);
444+
if (isLHSTrue && *OpName == "||")
445+
return lhs;
446+
if (!isLHSTrue && *OpName == "&&")
447+
return lhs;
448+
}
449+
450+
E->getArgs()->setExpr(0, lhs);
451+
}
452+
453+
if (auto rhs = validate(E->getRHS()))
454+
E->getArgs()->setExpr(1, rhs);
455+
456+
return E;
457+
}
458+
419459
// Fold sequence expression for non-Swift3 mode.
420460
Expr *visitSequenceExpr(SequenceExpr *E) {
421461
ArrayRef<Expr*> Elts = E->getElements();
422-
Expr *foldedExpr = validate(Elts[0]);
462+
Expr *foldedExpr = Elts[0];
423463
Elts = Elts.slice(1);
424464
foldedExpr = foldSequence(foldedExpr, Elts);
425465
assert(Elts.empty());
426-
return foldedExpr;
466+
return validate(foldedExpr);
427467
}
428468

429469
// Other expression types are unsupported.
@@ -484,6 +524,7 @@ class EvaluateIfConfigCondition :
484524
bool isKnownFeature = llvm::StringSwitch<bool>(Name)
485525
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option) \
486526
.Case("$" #FeatureName, Option)
527+
#define UPCOMING_FEATURE(FeatureName, SENumber, Version)
487528
#include "swift/Basic/Features.def"
488529
.Default(false);
489530

@@ -530,6 +571,9 @@ class EvaluateIfConfigCondition :
530571
ImportPath::Module::Builder builder(Ctx, Str, /*separator=*/'.',
531572
Arg->getStartLoc());
532573
return Ctx.canImportModule(builder.get(), version, underlyingModule);
574+
} else if (KindName == "hasFeature") {
575+
auto featureName = getDeclRefStr(Arg);
576+
return Ctx.LangOpts.hasFeature(featureName);
533577
}
534578

535579
auto Val = getDeclRefStr(Arg);
@@ -594,7 +638,6 @@ class IsVersionIfConfigCondition :
594638
bool visitExpr(Expr *E) { return false; }
595639
};
596640

597-
/// Returns \c true if the condition is a version check.
598641
static bool isVersionIfConfigCondition(Expr *Condition) {
599642
return IsVersionIfConfigCondition().visit(Condition);
600643
}

test/Frontend/upcoming_feature.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Make sure that hasFeature(ConciseMagicFile) evaluates true when provided
2+
// explicitly.
3+
// RUN: %target-swift-frontend -typecheck -enable-upcoming-feature ConciseMagicFile %s
4+
5+
// Make sure that hasFeature(ConciseMagicFile) evaluates true in Swift 6.
6+
// RUN: %target-swift-frontend -typecheck -swift-version 6 %s
7+
8+
// Make sure that hasFeature(ConciseMagicFile) is off prior to Swift 6
9+
// RUN: %target-typecheck-verify-swift %s
10+
11+
// It's fine to provide a feature that we don't know about
12+
// RUN: %target-swift-frontend -typecheck -enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature UnknownFeature %s
13+
// RUN: %target-swift-frontend -typecheck -enable-upcoming-feature UnknownFeature -enable-upcoming-feature ConciseMagicFile %s
14+
15+
16+
// It's not fine to provide a feature that's in the specified language version.
17+
// RUN: not %target-swift-frontend -typecheck -enable-upcoming-feature ConciseMagicFile -swift-version 6 %s 2>&1 | %FileCheck %s
18+
// REQUIRES: asserts
19+
20+
// CHECK: error: upcoming feature 'ConciseMagicFile' is already enabled as of Swift version 6
21+
22+
#if hasFeature(ConciseMagicFile)
23+
let x = 0
24+
#else
25+
let y = boom // expected-error{{'boom'}}
26+
#endif

test/Parse/unknown_platform.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// expected-error@+1{{unexpected platform condition}}
4+
#if hasGreeble(blah)
5+
#endif
6+
7+
// Future compiler, short-circuit right-hand side
8+
#if compiler(>=10.0) && hasGreeble(blah)
9+
#endif
10+
11+
// Current compiler, short-circuit right-hand side
12+
#if compiler(<10.0) || hasGreeble(blah)
13+
#endif
14+
15+
// This compiler, don't short-circuit.
16+
// expected-error@+1{{unexpected platform condition}}
17+
#if compiler(>=5.7) && hasGreeble(blah)
18+
#endif
19+
20+
// This compiler, don't short-circuit.
21+
// expected-error@+1{{unexpected platform condition}}
22+
#if compiler(<5.8) || hasGreeble(blah)
23+
#endif
24+
25+
// Not a "version" check, so don't short-circuit.
26+
// expected-error@+1{{unexpected platform condition}}
27+
#if os(macOS) && hasGreeble(blah)
28+
#endif

test/Parse/upcoming_feature.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// expected-error@+1{{unexpected platform condition argument: expected feature name}}
4+
#if hasFeature(17)
5+
#endif

test/Sema/diag_mismatched_magic_literals_swift6.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
// The future "Swift 6 mode" behavior is staged in behind `-enable-experimental-concise-pound-file`.
1+
// The upcoming "Swift 6 mode" behavior is staged in behind `-enable-experimental-concise-pound-file`.
22
// RUN: %target-typecheck-verify-swift -enable-experimental-concise-pound-file
3+
// RUN: %target-typecheck-verify-swift -enable-upcoming-feature ConciseMagicFile
34

45
// And is also available in Swift 6 mode on asserts compilers.
56
// RUN: %target-typecheck-verify-swift -swift-version 6

0 commit comments

Comments
 (0)