Skip to content

[SE-0362] Piecemeal adoption of upcoming language improvements #59055

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Basic/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 6 additions & 6 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
1 change: 1 addition & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down
8 changes: 4 additions & 4 deletions lib/Basic/LangOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
21 changes: 21 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))
Expand Down
53 changes: 48 additions & 5 deletions lib/Parse/ParseIfConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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*> {
Expand Down Expand Up @@ -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) {
Expand All @@ -226,7 +233,7 @@ class ValidateIfConfigCondition :

OpName = NextOpName;
Op = S[0];
RHS = validate(S[1]);
RHS = S[1];
S = S.slice(2);
}

Expand Down Expand Up @@ -329,6 +336,16 @@ class ValidateIfConfigCondition :
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()) {
Expand Down Expand Up @@ -416,14 +433,37 @@ class ValidateIfConfigCondition :
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.
Expand Down Expand Up @@ -484,6 +524,7 @@ class EvaluateIfConfigCondition :
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);

Expand Down Expand Up @@ -530,6 +571,9 @@ class EvaluateIfConfigCondition :
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);
Expand Down Expand Up @@ -594,7 +638,6 @@ class IsVersionIfConfigCondition :
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);
}
Expand Down
26 changes: 26 additions & 0 deletions test/Frontend/upcoming_feature.swift
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions test/Parse/unknown_platform.swift
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions test/Parse/upcoming_feature.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// RUN: %target-typecheck-verify-swift

// expected-error@+1{{unexpected platform condition argument: expected feature name}}
#if hasFeature(17)
#endif
3 changes: 2 additions & 1 deletion test/Sema/diag_mismatched_magic_literals_swift6.swift
Original file line number Diff line number Diff line change
@@ -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
Expand Down