Merge pull request #59055 from DougGregor/future-feature

[SE-0362] Piecemeal adoption of upcoming language improvements
This commit is contained in:
Doug Gregor
2022-07-20 12:26:00 -07:00
committed by GitHub
14 changed files with 165 additions and 17 deletions

View File

@@ -3,6 +3,17 @@ CHANGELOG
_**Note:** This is in reverse chronological order, so newer entries are added to the top._
## Swift 5.8
* [SE-0362][]:
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:
* `ConciseMagicFile` enables the new `#file` semantics in [SE-0274][].
* `ForwardTrailingClosures` disables the "backward" scanning behavior of [SE-0286][].
* `BareSlashRegexLiterals` enables the regex literal syntax of [SE-0352][].
Features can be detected in source code with `#if hasFeature(X)`.
## Swift 5.7
* The Swift compiler no longer warns about redundant requirements in generic declarations. For example,
@@ -9459,6 +9470,7 @@ Swift 1.0
[SE-0267]: <https://github.com/apple/swift-evolution/blob/main/proposals/0267-where-on-contextually-generic.md>
[SE-0268]: <https://github.com/apple/swift-evolution/blob/main/proposals/0268-didset-semantics.md>
[SE-0269]: <https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md>
[SE-0274]: <https://github.com/apple/swift-evolution/blob/main/proposals/0274-magic-file.md>
[SE-0276]: <https://github.com/apple/swift-evolution/blob/main/proposals/0276-multi-pattern-catch-clauses.md>
[SE-0279]: <https://github.com/apple/swift-evolution/blob/main/proposals/0279-multiple-trailing-closures.md>
[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
[SE-0355]: <https://github.com/apple/swift-evolution/blob/main/proposals/0355-regex-syntax-run-time-construction.md>
[SE-0357]: <https://github.com/apple/swift-evolution/blob/main/proposals/0357-regex-string-processing-algorithms.md>
[SE-0358]: <https://github.com/apple/swift-evolution/blob/main/proposals/0358-primary-associated-types-in-stdlib.md>
[SE-0362]: <https://github.com/apple/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md>
[SR-75]: <https://bugs.swift.org/browse/SR-75>
[SR-106]: <https://bugs.swift.org/browse/SR-106>

View File

@@ -40,6 +40,10 @@ ERROR(error_experimental_feature_not_available, none,
"experimental feature '%0' cannot be enabled in a production compiler",
(StringRef))
ERROR(error_upcoming_feature_on_by_default, none,
"upcoming feature '%0' is already enabled as of Swift version %1",
(StringRef, unsigned))
ERROR(error_unknown_library_level, none,
"unknown library level '%0', "
"expected one of 'api', 'spi' or 'other'", (StringRef))

View File

@@ -53,7 +53,7 @@ inline bool featureImpliesFeature(Feature feature, Feature implied) {
}
/// Get the feature corresponding to this "future" feature, if there is one.
llvm::Optional<Feature> getFutureFeature(llvm::StringRef name);
llvm::Optional<Feature> getUpcomingFeature(llvm::StringRef name);
/// Get the feature corresponding to this "experimental" feature, if there is
/// one.

View File

@@ -48,8 +48,8 @@
LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option)
#endif
#ifndef FUTURE_FEATURE
# define FUTURE_FEATURE(FeatureName, SENumber, Version) \
#ifndef UPCOMING_FEATURE
# define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
LANGUAGE_FEATURE(FeatureName, SENumber, #FeatureName, \
langOpts.hasFeature(#FeatureName))
#endif
@@ -90,9 +90,9 @@ SUPPRESSIBLE_LANGUAGE_FEATURE(PrimaryAssociatedTypes2, 346, "Primary associated
SUPPRESSIBLE_LANGUAGE_FEATURE(UnavailableFromAsync, 0, "@_unavailableFromAsync", true)
SUPPRESSIBLE_LANGUAGE_FEATURE(NoAsyncAvailability, 340, "@available(*, noasync)", true)
FUTURE_FEATURE(ConciseMagicFile, 274, 6)
FUTURE_FEATURE(ForwardTrailingClosures, 286, 6)
FUTURE_FEATURE(BareSlashRegexLiterals, 354, 6)
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)
UPCOMING_FEATURE(BareSlashRegexLiterals, 354, 6)
EXPERIMENTAL_FEATURE(StaticAssert)
EXPERIMENTAL_FEATURE(VariadicGenerics)
@@ -118,6 +118,6 @@ EXPERIMENTAL_FEATURE(AdditiveArithmeticDerivedConformances)
EXPERIMENTAL_FEATURE(SendableCompletionHandlers)
#undef EXPERIMENTAL_FEATURE
#undef FUTURE_FEATURE
#undef UPCOMING_FEATURE
#undef SUPPRESSIBLE_LANGUAGE_FEATURE
#undef LANGUAGE_FEATURE

View File

@@ -28,6 +28,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Regex.h"

View File

@@ -696,6 +696,11 @@ def enable_experimental_feature :
Flags<[FrontendOption]>,
HelpText<"Enable an experimental feature">;
def enable_upcoming_feature : Separate<["-"], "enable-upcoming-feature">,
Flags<[FrontendOption]>,
HelpText<"Enable a feature that will be introduced in an upcoming language "
"version">;
def Rpass_EQ : Joined<["-"], "Rpass=">,
Flags<[FrontendOption]>,
HelpText<"Report performed transformations by optimization passes whose "

View File

@@ -240,7 +240,7 @@ bool LangOptions::hasFeature(Feature feature) const {
}
bool LangOptions::hasFeature(llvm::StringRef featureName) const {
if (auto feature = getFutureFeature(featureName))
if (auto feature = getUpcomingFeature(featureName))
return hasFeature(*feature);
if (auto feature = getExperimentalFeature(featureName))
@@ -434,10 +434,10 @@ bool swift::isSuppressibleFeature(Feature feature) {
llvm_unreachable("covered switch");
}
llvm::Optional<Feature> swift::getFutureFeature(llvm::StringRef name) {
llvm::Optional<Feature> swift::getUpcomingFeature(llvm::StringRef name) {
return llvm::StringSwitch<Optional<Feature>>(name)
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option)
#define FUTURE_FEATURE(FeatureName, SENumber, Version) \
#define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
.Case(#FeatureName, Feature::FeatureName)
#include "swift/Basic/Features.def"
.Default(None);
@@ -455,7 +455,7 @@ llvm::Optional<Feature> swift::getExperimentalFeature(llvm::StringRef name) {
llvm::Optional<unsigned> swift::getFeatureLanguageVersion(Feature feature) {
switch (feature) {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option)
#define FUTURE_FEATURE(FeatureName, SENumber, Version) \
#define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
case Feature::FeatureName: return Version;
#include "swift/Basic/Features.def"
default: return None;

View File

@@ -237,6 +237,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
inputArgs.AddLastArg(arguments, options::OPT_warn_concurrency);
inputArgs.AddLastArg(arguments, options::OPT_strict_concurrency);
inputArgs.AddAllArgs(arguments, options::OPT_enable_experimental_feature);
inputArgs.AddAllArgs(arguments, options::OPT_enable_upcoming_feature);
inputArgs.AddLastArg(arguments, options::OPT_warn_implicit_overrides);
inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit);
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);

View File

@@ -15,6 +15,7 @@
#include "ArgsToFrontendOptionsConverter.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/Feature.h"
#include "swift/Basic/Platform.h"
#include "swift/Option/Options.h"
#include "swift/Option/SanitizerOptions.h"
@@ -637,6 +638,26 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
}
}
// Map historical flags over to future features.
for (const Arg *A : Args.filtered(OPT_enable_upcoming_feature)) {
// Ignore unknown features.
auto feature = getUpcomingFeature(A->getValue());
if (!feature)
continue;
// Check if this feature was introduced already in this language version.
if (auto firstVersion = getFeatureLanguageVersion(*feature)) {
if (Opts.isSwiftVersionAtLeast(*firstVersion)) {
Diags.diagnose(SourceLoc(), diag::error_upcoming_feature_on_by_default,
A->getValue(), *firstVersion);
continue;
}
}
// Add the feature.
Opts.Features.insert(*feature);
}
// Map historical flags over to experimental features. We do this for all
// compilers because that's how existing experimental feature flags work.
if (Args.hasArg(OPT_enable_experimental_variadic_generics))

View File

@@ -133,6 +133,13 @@ static Expr *getSingleSubExp(ArgumentList *args, StringRef kindName,
return nullptr;
}
/// Returns \c true if the condition is a version check.
static bool isVersionIfConfigCondition(Expr *Condition);
/// Evaluate the condition.
/// \c true if success, \c false if failed.
static bool evaluateIfConfigCondition(Expr *Condition, ASTContext &Context);
/// The condition validator.
class ValidateIfConfigCondition :
public ExprVisitor<ValidateIfConfigCondition, Expr*> {
@@ -202,7 +209,7 @@ class ValidateIfConfigCondition :
// We will definitely be consuming at least one operator.
// Pull out the prospective RHS and slice off the first two elements.
Expr *RHS = validate(S[1]);
Expr *RHS = S[1];
S = S.slice(2);
while (true) {
@@ -226,7 +233,7 @@ class ValidateIfConfigCondition :
OpName = NextOpName;
Op = S[0];
RHS = validate(S[1]);
RHS = S[1];
S = S.slice(2);
}
@@ -329,6 +336,16 @@ public:
return E;
}
if (*KindName == "hasFeature") {
if (!getDeclRefStr(Arg, DeclRefKind::Ordinary)) {
D.diagnose(E->getLoc(), diag::unsupported_platform_condition_argument,
"feature name");
return nullptr;
}
return E;
}
// ( 'os' | 'arch' | '_endian' | '_runtime' ) '(' identifier ')''
auto Kind = getPlatformConditionKind(*KindName);
if (!Kind.hasValue()) {
@@ -416,14 +433,37 @@ public:
return E;
}
Expr *visitBinaryExpr(BinaryExpr *E) {
auto OpName = getDeclRefStr(E->getFn(), DeclRefKind::BinaryOperator);
if (auto lhs = validate(E->getLHS())) {
// If the left-hand side is a versioned condition, skip evaluation of
// the right-hand side if it won't ever affect the result.
if (OpName && isVersionIfConfigCondition(lhs)) {
assert(*OpName == "&&" || *OpName == "||");
bool isLHSTrue = evaluateIfConfigCondition(lhs, Ctx);
if (isLHSTrue && *OpName == "||")
return lhs;
if (!isLHSTrue && *OpName == "&&")
return lhs;
}
E->getArgs()->setExpr(0, lhs);
}
if (auto rhs = validate(E->getRHS()))
E->getArgs()->setExpr(1, rhs);
return E;
}
// Fold sequence expression for non-Swift3 mode.
Expr *visitSequenceExpr(SequenceExpr *E) {
ArrayRef<Expr*> Elts = E->getElements();
Expr *foldedExpr = validate(Elts[0]);
Expr *foldedExpr = Elts[0];
Elts = Elts.slice(1);
foldedExpr = foldSequence(foldedExpr, Elts);
assert(Elts.empty());
return foldedExpr;
return validate(foldedExpr);
}
// Other expression types are unsupported.
@@ -484,6 +524,7 @@ public:
bool isKnownFeature = llvm::StringSwitch<bool>(Name)
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option) \
.Case("$" #FeatureName, Option)
#define UPCOMING_FEATURE(FeatureName, SENumber, Version)
#include "swift/Basic/Features.def"
.Default(false);
@@ -530,6 +571,9 @@ public:
ImportPath::Module::Builder builder(Ctx, Str, /*separator=*/'.',
Arg->getStartLoc());
return Ctx.canImportModule(builder.get(), version, underlyingModule);
} else if (KindName == "hasFeature") {
auto featureName = getDeclRefStr(Arg);
return Ctx.LangOpts.hasFeature(featureName);
}
auto Val = getDeclRefStr(Arg);
@@ -594,7 +638,6 @@ public:
bool visitExpr(Expr *E) { return false; }
};
/// Returns \c true if the condition is a version check.
static bool isVersionIfConfigCondition(Expr *Condition) {
return IsVersionIfConfigCondition().visit(Condition);
}

View File

@@ -0,0 +1,26 @@
// Make sure that hasFeature(ConciseMagicFile) evaluates true when provided
// explicitly.
// RUN: %target-swift-frontend -typecheck -enable-upcoming-feature ConciseMagicFile %s
// Make sure that hasFeature(ConciseMagicFile) evaluates true in Swift 6.
// RUN: %target-swift-frontend -typecheck -swift-version 6 %s
// Make sure that hasFeature(ConciseMagicFile) is off prior to Swift 6
// RUN: %target-typecheck-verify-swift %s
// It's fine to provide a feature that we don't know about
// RUN: %target-swift-frontend -typecheck -enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature UnknownFeature %s
// RUN: %target-swift-frontend -typecheck -enable-upcoming-feature UnknownFeature -enable-upcoming-feature ConciseMagicFile %s
// It's not fine to provide a feature that's in the specified language version.
// RUN: not %target-swift-frontend -typecheck -enable-upcoming-feature ConciseMagicFile -swift-version 6 %s 2>&1 | %FileCheck %s
// REQUIRES: asserts
// CHECK: error: upcoming feature 'ConciseMagicFile' is already enabled as of Swift version 6
#if hasFeature(ConciseMagicFile)
let x = 0
#else
let y = boom // expected-error{{'boom'}}
#endif

View File

@@ -0,0 +1,28 @@
// RUN: %target-typecheck-verify-swift
// expected-error@+1{{unexpected platform condition}}
#if hasGreeble(blah)
#endif
// Future compiler, short-circuit right-hand side
#if compiler(>=10.0) && hasGreeble(blah)
#endif
// Current compiler, short-circuit right-hand side
#if compiler(<10.0) || hasGreeble(blah)
#endif
// This compiler, don't short-circuit.
// expected-error@+1{{unexpected platform condition}}
#if compiler(>=5.7) && hasGreeble(blah)
#endif
// This compiler, don't short-circuit.
// expected-error@+1{{unexpected platform condition}}
#if compiler(<5.8) || hasGreeble(blah)
#endif
// Not a "version" check, so don't short-circuit.
// expected-error@+1{{unexpected platform condition}}
#if os(macOS) && hasGreeble(blah)
#endif

View File

@@ -0,0 +1,5 @@
// RUN: %target-typecheck-verify-swift
// expected-error@+1{{unexpected platform condition argument: expected feature name}}
#if hasFeature(17)
#endif

View File

@@ -1,5 +1,6 @@
// The future "Swift 6 mode" behavior is staged in behind `-enable-experimental-concise-pound-file`.
// The upcoming "Swift 6 mode" behavior is staged in behind `-enable-experimental-concise-pound-file`.
// RUN: %target-typecheck-verify-swift -enable-experimental-concise-pound-file
// RUN: %target-typecheck-verify-swift -enable-upcoming-feature ConciseMagicFile
// And is also available in Swift 6 mode on asserts compilers.
// RUN: %target-typecheck-verify-swift -swift-version 6